{
    "file_diffs": [
        {
            "old_file_content": "",
            "new_file_content": "Fixed regression with adding Content-Disposition to form-data part after appending to writer -- by :user:`Dreamsorcerer`/:user:`Olegt0rr`.\n",
            "header": {
                "file": {
                    "path": "CHANGES/8332.bugfix.rst"
                },
                "misc_line": "new file mode 100644"
            },
            "index_line": {
                "old_commit_hash": "00000000",
                "new_commit_hash": "70cad26b",
                "mode": ""
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "/dev/null"
            },
            "plus_file": {
                "path": "b/CHANGES/8332.bugfix.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 0,
                            "length": 0
                        },
                        "new_range": {
                            "start": 1,
                            "length": null
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Fixed regression with adding Content-Disposition to form-data part after appending to writer -- by :user:`Dreamsorcerer`/:user:`Olegt0rr`.",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import base64\nimport binascii\nimport json\nimport re\nimport uuid\nimport warnings\nimport zlib\nfrom collections import deque\nfrom types import TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Deque,\n    Dict,\n    Iterator,\n    List,\n    Mapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\nfrom urllib.parse import parse_qsl, unquote, urlencode\n\nfrom multidict import CIMultiDict, CIMultiDictProxy\n\nfrom .compression_utils import ZLibCompressor, ZLibDecompressor\nfrom .hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_LENGTH,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom .helpers import CHAR, TOKEN, parse_mimetype, reify\nfrom .http import HeadersParser\nfrom .payload import (\n    JsonPayload,\n    LookupError,\n    Order,\n    Payload,\n    StringPayload,\n    get_payload,\n    payload_type,\n)\nfrom .streams import StreamReader\n\n__all__ = (\n    \"MultipartReader\",\n    \"MultipartWriter\",\n    \"BodyPartReader\",\n    \"BadContentDispositionHeader\",\n    \"BadContentDispositionParam\",\n    \"parse_content_disposition\",\n    \"content_disposition_filename\",\n)\n\n\nif TYPE_CHECKING:\n    from .client_reqrep import ClientResponse\n\n\nclass BadContentDispositionHeader(RuntimeWarning):\n    pass\n\n\nclass BadContentDispositionParam(RuntimeWarning):\n    pass\n\n\ndef parse_content_disposition(\n    header: Optional[str],\n) -> Tuple[Optional[str], Dict[str, str]]:\n    def is_token(string: str) -> bool:\n        return bool(string) and TOKEN >= set(string)\n\n    def is_quoted(string: str) -> bool:\n        return string[0] == string[-1] == '\"'\n\n    def is_rfc5987(string: str) -> bool:\n        return is_token(string) and string.count(\"'\") == 2\n\n    def is_extended_param(string: str) -> bool:\n        return string.endswith(\"*\")\n\n    def is_continuous_param(string: str) -> bool:\n        pos = string.find(\"*\") + 1\n        if not pos:\n            return False\n        substring = string[pos:-1] if string.endswith(\"*\") else string[pos:]\n        return substring.isdigit()\n\n    def unescape(text: str, *, chars: str = \"\".join(map(re.escape, CHAR))) -> str:\n        return re.sub(f\"\\\\\\\\([{chars}])\", \"\\\\1\", text)\n\n    if not header:\n        return None, {}\n\n    disptype, *parts = header.split(\";\")\n    if not is_token(disptype):\n        warnings.warn(BadContentDispositionHeader(header))\n        return None, {}\n\n    params: Dict[str, str] = {}\n    while parts:\n        item = parts.pop(0)\n\n        if \"=\" not in item:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        key, value = item.split(\"=\", 1)\n        key = key.lower().strip()\n        value = value.lstrip()\n\n        if key in params:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        if not is_token(key):\n            warnings.warn(BadContentDispositionParam(item))\n            continue\n\n        elif is_continuous_param(key):\n            if is_quoted(value):\n                value = unescape(value[1:-1])\n            elif not is_token(value):\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        elif is_extended_param(key):\n            if is_rfc5987(value):\n                encoding, _, value = value.split(\"'\", 2)\n                encoding = encoding or \"utf-8\"\n            else:\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n            try:\n                value = unquote(value, encoding, \"strict\")\n            except UnicodeDecodeError:  # pragma: nocover\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        else:\n            failed = True\n            if is_quoted(value):\n                failed = False\n                value = unescape(value[1:-1].lstrip(\"\\\\/\"))\n            elif is_token(value):\n                failed = False\n            elif parts:\n                # maybe just ; in filename, in any case this is just\n                # one case fix, for proper fix we need to redesign parser\n                _value = f\"{value};{parts[0]}\"\n                if is_quoted(_value):\n                    parts.pop(0)\n                    value = unescape(_value[1:-1].lstrip(\"\\\\/\"))\n                    failed = False\n\n            if failed:\n                warnings.warn(BadContentDispositionHeader(header))\n                return None, {}\n\n        params[key] = value\n\n    return disptype.lower(), params\n\n\ndef content_disposition_filename(\n    params: Mapping[str, str], name: str = \"filename\"\n) -> Optional[str]:\n    name_suf = \"%s*\" % name\n    if not params:\n        return None\n    elif name_suf in params:\n        return params[name_suf]\n    elif name in params:\n        return params[name]\n    else:\n        parts = []\n        fnparams = sorted(\n            (key, value) for key, value in params.items() if key.startswith(name_suf)\n        )\n        for num, (key, value) in enumerate(fnparams):\n            _, tail = key.split(\"*\", 1)\n            if tail.endswith(\"*\"):\n                tail = tail[:-1]\n            if tail == str(num):\n                parts.append(value)\n            else:\n                break\n        if not parts:\n            return None\n        value = \"\".join(parts)\n        if \"'\" in value:\n            encoding, _, value = value.split(\"'\", 2)\n            encoding = encoding or \"utf-8\"\n            return unquote(value, encoding, \"strict\")\n        return value\n\n\nclass MultipartResponseWrapper:\n    \"\"\"Wrapper around the MultipartReader.\n\n    It takes care about\n    underlying connection and close it when it needs in.\n    \"\"\"\n\n    def __init__(\n        self,\n        resp: \"ClientResponse\",\n        stream: \"MultipartReader\",\n    ) -> None:\n        self.resp = resp\n        self.stream = stream\n\n    def __aiter__(self) -> \"MultipartResponseWrapper\":\n        return self\n\n    async def __anext__(\n        self,\n    ) -> Union[\"MultipartReader\", \"BodyPartReader\"]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True when all response data had been read.\"\"\"\n        return self.resp.content.at_eof()\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", \"BodyPartReader\"]]:\n        \"\"\"Emits next multipart reader object.\"\"\"\n        item = await self.stream.next()\n        if self.stream.at_eof():\n            await self.release()\n        return item\n\n    async def release(self) -> None:\n        \"\"\"Release the connection gracefully.\n\n        All remaining content is read to the void.\n        \"\"\"\n        await self.resp.release()\n\n\nclass BodyPartReader:\n    \"\"\"Multipart reader for single body part.\"\"\"\n\n    chunk_size = 8192\n\n    def __init__(\n        self,\n        boundary: bytes,\n        headers: \"CIMultiDictProxy[str]\",\n        content: StreamReader,\n        *,\n        subtype: str = \"mixed\",\n        default_charset: Optional[str] = None,\n    ) -> None:\n        self.headers = headers\n        self._boundary = boundary\n        self._content = content\n        self._default_charset = default_charset\n        self._at_eof = False\n        self._is_form_data = subtype == \"form-data\"\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)\n        self._length = int(length) if length is not None else None\n        self._read_bytes = 0\n        self._unread: Deque[bytes] = deque()\n        self._prev_chunk: Optional[bytes] = None\n        self._content_eof = 0\n        self._cache: Dict[str, Any] = {}\n\n    def __aiter__(self) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(self) -> bytes:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    async def next(self) -> Optional[bytes]:\n        item = await self.read()\n        if not item:\n            return None\n        return item\n\n    async def read(self, *, decode: bool = False) -> bytes:\n        \"\"\"Reads body part data.\n\n        decode: Decodes data following by encoding\n                method from Content-Encoding header. If it missed\n                data remains untouched\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        data = bytearray()\n        while not self._at_eof:\n            data.extend(await self.read_chunk(self.chunk_size))\n        if decode:\n            return self.decode(data)\n        return data\n\n    async def read_chunk(self, size: int = chunk_size) -> bytes:\n        \"\"\"Reads body part content chunk of the specified size.\n\n        size: chunk size\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        if self._length:\n            chunk = await self._read_chunk_from_length(size)\n        else:\n            chunk = await self._read_chunk_from_stream(size)\n\n        self._read_bytes += len(chunk)\n        if self._read_bytes == self._length:\n            self._at_eof = True\n        if self._at_eof:\n            clrf = await self._content.readline()\n            assert (\n                b\"\\r\\n\" == clrf\n            ), \"reader did not read all the data or it is malformed\"\n        return chunk\n\n    async def _read_chunk_from_length(self, size: int) -> bytes:\n        # Reads body part content chunk of the specified size.\n        # The body part must has Content-Length header with proper value.\n        assert self._length is not None, \"Content-Length required for chunked read\"\n        chunk_size = min(size, self._length - self._read_bytes)\n        chunk = await self._content.read(chunk_size)\n        if self._content.at_eof():\n            self._at_eof = True\n        return chunk\n\n    async def _read_chunk_from_stream(self, size: int) -> bytes:\n        # Reads content chunk of body part with unknown length.\n        # The Content-Length header for body part is not necessary.\n        assert (\n            size >= len(self._boundary) + 2\n        ), \"Chunk size must be greater or equal than boundary length + 2\"\n        first_chunk = self._prev_chunk is None\n        if first_chunk:\n            self._prev_chunk = await self._content.read(size)\n\n        chunk = await self._content.read(size)\n        self._content_eof += int(self._content.at_eof())\n        assert self._content_eof < 3, \"Reading after EOF\"\n        assert self._prev_chunk is not None\n        window = self._prev_chunk + chunk\n        sub = b\"\\r\\n\" + self._boundary\n        if first_chunk:\n            idx = window.find(sub)\n        else:\n            idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub)))\n        if idx >= 0:\n            # pushing boundary back to content\n            with warnings.catch_warnings():\n                warnings.filterwarnings(\"ignore\", category=DeprecationWarning)\n                self._content.unread_data(window[idx:])\n            if size > idx:\n                self._prev_chunk = self._prev_chunk[:idx]\n            chunk = window[len(self._prev_chunk) : idx]\n            if not chunk:\n                self._at_eof = True\n        result = self._prev_chunk\n        self._prev_chunk = chunk\n        return result\n\n    async def readline(self) -> bytes:\n        \"\"\"Reads body part by line by line.\"\"\"\n        if self._at_eof:\n            return b\"\"\n\n        if self._unread:\n            line = self._unread.popleft()\n        else:\n            line = await self._content.readline()\n\n        if line.startswith(self._boundary):\n            # the very last boundary may not come with \\r\\n,\n            # so set single rules for everyone\n            sline = line.rstrip(b\"\\r\\n\")\n            boundary = self._boundary\n            last_boundary = self._boundary + b\"--\"\n            # ensure that we read exactly the boundary, not something alike\n            if sline == boundary or sline == last_boundary:\n                self._at_eof = True\n                self._unread.append(line)\n                return b\"\"\n        else:\n            next_line = await self._content.readline()\n            if next_line.startswith(self._boundary):\n                line = line[:-2]  # strip CRLF but only once\n            self._unread.append(next_line)\n\n        return line\n\n    async def release(self) -> None:\n        \"\"\"Like read(), but reads all the data to the void.\"\"\"\n        if self._at_eof:\n            return\n        while not self._at_eof:\n            await self.read_chunk(self.chunk_size)\n\n    async def text(self, *, encoding: Optional[str] = None) -> str:\n        \"\"\"Like read(), but assumes that body part contains text data.\"\"\"\n        data = await self.read(decode=True)\n        # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm\n        # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return data.decode(encoding)\n\n    async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]:\n        \"\"\"Like read(), but assumes that body parts contains JSON data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return None\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return cast(Dict[str, Any], json.loads(data.decode(encoding)))\n\n    async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:\n        \"\"\"Like read(), but assumes that body parts contain form urlencoded data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return []\n        if encoding is not None:\n            real_encoding = encoding\n        else:\n            real_encoding = self.get_charset(default=\"utf-8\")\n        try:\n            decoded_data = data.rstrip().decode(real_encoding)\n        except UnicodeDecodeError:\n            raise ValueError(\"data cannot be decoded with %s encoding\" % real_encoding)\n\n        return parse_qsl(\n            decoded_data,\n            keep_blank_values=True,\n            encoding=real_encoding,\n        )\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the boundary was reached or False otherwise.\"\"\"\n        return self._at_eof\n\n    def decode(self, data: bytes) -> bytes:\n        \"\"\"Decodes data.\n\n        Decoding is done according the specified Content-Encoding\n        or Content-Transfer-Encoding headers value.\n        \"\"\"\n        if CONTENT_TRANSFER_ENCODING in self.headers:\n            data = self._decode_content_transfer(data)\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        if not self._is_form_data and CONTENT_ENCODING in self.headers:\n            return self._decode_content(data)\n        return data\n\n    def _decode_content(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_ENCODING, \"\").lower()\n        if encoding == \"identity\":\n            return data\n        if encoding in {\"deflate\", \"gzip\"}:\n            return ZLibDecompressor(\n                encoding=encoding,\n                suppress_deflate_header=True,\n            ).decompress_sync(data)\n\n        raise RuntimeError(f\"unknown content encoding: {encoding}\")\n\n    def _decode_content_transfer(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n\n        if encoding == \"base64\":\n            return base64.b64decode(data)\n        elif encoding == \"quoted-printable\":\n            return binascii.a2b_qp(data)\n        elif encoding in (\"binary\", \"8bit\", \"7bit\"):\n            return data\n        else:\n            raise RuntimeError(\n                \"unknown content transfer encoding: {}\" \"\".format(encoding)\n            )\n\n    def get_charset(self, default: str) -> str:\n        \"\"\"Returns charset parameter from Content-Type header or default.\"\"\"\n        ctype = self.headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n        return mimetype.parameters.get(\"charset\", self._default_charset or default)\n\n    @reify\n    def name(self) -> Optional[str]:\n        \"\"\"Returns name specified in Content-Disposition header.\n\n        If the header is missing or malformed, returns None.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"name\")\n\n    @reify\n    def filename(self) -> Optional[str]:\n        \"\"\"Returns filename specified in Content-Disposition header.\n\n        Returns None if the header is missing or malformed.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"filename\")\n\n\n@payload_type(BodyPartReader, order=Order.try_first)\nclass BodyPartReaderPayload(Payload):\n    def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:\n        super().__init__(value, *args, **kwargs)\n\n        params: Dict[str, str] = {}\n        if value.name is not None:\n            params[\"name\"] = value.name\n        if value.filename is not None:\n            params[\"filename\"] = value.filename\n\n        if params:\n            self.set_content_disposition(\"attachment\", True, **params)\n\n    async def write(self, writer: Any) -> None:\n        field = self._value\n        chunk = await field.read_chunk(size=2**16)\n        while chunk:\n            await writer.write(field.decode(chunk))\n            chunk = await field.read_chunk(size=2**16)\n\n\nclass MultipartReader:\n    \"\"\"Multipart body reader.\"\"\"\n\n    #: Response wrapper, used when multipart readers constructs from response.\n    response_wrapper_cls = MultipartResponseWrapper\n    #: Multipart reader class, used to handle multipart/* body parts.\n    #: None points to type(self)\n    multipart_reader_cls = None\n    #: Body part reader class for non multipart/* content types.\n    part_reader_cls = BodyPartReader\n\n    def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None:\n        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])\n        assert self._mimetype.type == \"multipart\", \"multipart/* content type expected\"\n        if \"boundary\" not in self._mimetype.parameters:\n            raise ValueError(\n                \"boundary missed for Content-Type: %s\" % headers[CONTENT_TYPE]\n            )\n\n        self.headers = headers\n        self._boundary = (\"--\" + self._get_boundary()).encode()\n        self._content = content\n        self._default_charset: Optional[str] = None\n        self._last_part: Optional[Union[\"MultipartReader\", BodyPartReader]] = None\n        self._at_eof = False\n        self._at_bof = True\n        self._unread: List[bytes] = []\n\n    def __aiter__(\n        self,\n    ) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    @classmethod\n    def from_response(\n        cls,\n        response: \"ClientResponse\",\n    ) -> MultipartResponseWrapper:\n        \"\"\"Constructs reader instance from HTTP response.\n\n        :param response: :class:`~aiohttp.client.ClientResponse` instance\n        \"\"\"\n        obj = cls.response_wrapper_cls(\n            response, cls(response.headers, response.content)\n        )\n        return obj\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the final boundary was reached, false otherwise.\"\"\"\n        return self._at_eof\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        \"\"\"Emits the next multipart body part.\"\"\"\n        # So, if we're at BOF, we need to skip till the boundary.\n        if self._at_eof:\n            return None\n        await self._maybe_release_last_part()\n        if self._at_bof:\n            await self._read_until_first_boundary()\n            self._at_bof = False\n        else:\n            await self._read_boundary()\n        if self._at_eof:  # we just read the last boundary, nothing to do there\n            return None\n\n        part = await self.fetch_next_part()\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6\n        if (\n            self._last_part is None\n            and self._mimetype.subtype == \"form-data\"\n            and isinstance(part, BodyPartReader)\n        ):\n            _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION))\n            if params.get(\"name\") == \"_charset_\":\n                # Longest encoding in https://encoding.spec.whatwg.org/encodings.json\n                # is 19 characters, so 32 should be more than enough for any valid encoding.\n                charset = await part.read_chunk(32)\n                if len(charset) > 31:\n                    raise RuntimeError(\"Invalid default charset\")\n                self._default_charset = charset.strip().decode()\n                part = await self.fetch_next_part()\n        self._last_part = part\n        return self._last_part\n\n    async def release(self) -> None:\n        \"\"\"Reads all the body parts to the void till the final boundary.\"\"\"\n        while not self._at_eof:\n            item = await self.next()\n            if item is None:\n                break\n            await item.release()\n\n    async def fetch_next_part(\n        self,\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Returns the next body part reader.\"\"\"\n        headers = await self._read_headers()\n        return self._get_part_reader(headers)\n\n    def _get_part_reader(\n        self,\n        headers: \"CIMultiDictProxy[str]\",\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Dispatches the response by the `Content-Type` header.\n\n        Returns a suitable reader instance.\n\n        :param dict headers: Response headers\n        \"\"\"\n        ctype = headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n\n        if mimetype.type == \"multipart\":\n            if self.multipart_reader_cls is None:\n                return type(self)(headers, self._content)\n            return self.multipart_reader_cls(headers, self._content)\n        else:\n            return self.part_reader_cls(\n                self._boundary,\n                headers,\n                self._content,\n                subtype=self._mimetype.subtype,\n                default_charset=self._default_charset,\n            )\n\n    def _get_boundary(self) -> str:\n        boundary = self._mimetype.parameters[\"boundary\"]\n        if len(boundary) > 70:\n            raise ValueError(\"boundary %r is too long (70 chars max)\" % boundary)\n\n        return boundary\n\n    async def _readline(self) -> bytes:\n        if self._unread:\n            return self._unread.pop()\n        return await self._content.readline()\n\n    async def _read_until_first_boundary(self) -> None:\n        while True:\n            chunk = await self._readline()\n            if chunk == b\"\":\n                raise ValueError(\n                    \"Could not find starting boundary %r\" % (self._boundary)\n                )\n            chunk = chunk.rstrip()\n            if chunk == self._boundary:\n                return\n            elif chunk == self._boundary + b\"--\":\n                self._at_eof = True\n                return\n\n    async def _read_boundary(self) -> None:\n        chunk = (await self._readline()).rstrip()\n        if chunk == self._boundary:\n            pass\n        elif chunk == self._boundary + b\"--\":\n            self._at_eof = True\n            epilogue = await self._readline()\n            next_line = await self._readline()\n\n            # the epilogue is expected and then either the end of input or the\n            # parent multipart boundary, if the parent boundary is found then\n            # it should be marked as unread and handed to the parent for\n            # processing\n            if next_line[:2] == b\"--\":\n                self._unread.append(next_line)\n            # otherwise the request is likely missing an epilogue and both\n            # lines should be passed to the parent for processing\n            # (this handles the old behavior gracefully)\n            else:\n                self._unread.extend([next_line, epilogue])\n        else:\n            raise ValueError(f\"Invalid boundary {chunk!r}, expected {self._boundary!r}\")\n\n    async def _read_headers(self) -> \"CIMultiDictProxy[str]\":\n        lines = [b\"\"]\n        while True:\n            chunk = await self._content.readline()\n            chunk = chunk.strip()\n            lines.append(chunk)\n            if not chunk:\n                break\n        parser = HeadersParser()\n        headers, raw_headers = parser.parse_headers(lines)\n        return headers\n\n    async def _maybe_release_last_part(self) -> None:\n        \"\"\"Ensures that the last read body part is read completely.\"\"\"\n        if self._last_part is not None:\n            if not self._last_part.at_eof():\n                await self._last_part.release()\n            self._unread.extend(self._last_part._unread)\n            self._last_part = None\n\n\n_Part = Tuple[Payload, str, str]\n\n\nclass MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert CONTENT_DISPOSITION in payload.headers\n            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")\n\n\nclass MultipartPayloadWriter:\n    def __init__(self, writer: Any) -> None:\n        self._writer = writer\n        self._encoding: Optional[str] = None\n        self._compress: Optional[ZLibCompressor] = None\n        self._encoding_buffer: Optional[bytearray] = None\n\n    def enable_encoding(self, encoding: str) -> None:\n        if encoding == \"base64\":\n            self._encoding = encoding\n            self._encoding_buffer = bytearray()\n        elif encoding == \"quoted-printable\":\n            self._encoding = \"quoted-printable\"\n\n    def enable_compression(\n        self, encoding: str = \"deflate\", strategy: int = zlib.Z_DEFAULT_STRATEGY\n    ) -> None:\n        self._compress = ZLibCompressor(\n            encoding=encoding,\n            suppress_deflate_header=True,\n            strategy=strategy,\n        )\n\n    async def write_eof(self) -> None:\n        if self._compress is not None:\n            chunk = self._compress.flush()\n            if chunk:\n                self._compress = None\n                await self.write(chunk)\n\n        if self._encoding == \"base64\":\n            if self._encoding_buffer:\n                await self._writer.write(base64.b64encode(self._encoding_buffer))\n\n    async def write(self, chunk: bytes) -> None:\n        if self._compress is not None:\n            if chunk:\n                chunk = await self._compress.compress(chunk)\n                if not chunk:\n                    return\n\n        if self._encoding == \"base64\":\n            buf = self._encoding_buffer\n            assert buf is not None\n            buf.extend(chunk)\n\n            if buf:\n                div, mod = divmod(len(buf), 3)\n                enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :])\n                if enc_chunk:\n                    b64chunk = base64.b64encode(enc_chunk)\n                    await self._writer.write(b64chunk)\n        elif self._encoding == \"quoted-printable\":\n            await self._writer.write(binascii.b2a_qp(chunk))\n        else:\n            await self._writer.write(chunk)\n",
            "new_file_content": "import base64\nimport binascii\nimport json\nimport re\nimport uuid\nimport warnings\nimport zlib\nfrom collections import deque\nfrom types import TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Deque,\n    Dict,\n    Iterator,\n    List,\n    Mapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\nfrom urllib.parse import parse_qsl, unquote, urlencode\n\nfrom multidict import CIMultiDict, CIMultiDictProxy\n\nfrom .compression_utils import ZLibCompressor, ZLibDecompressor\nfrom .hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_LENGTH,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom .helpers import CHAR, TOKEN, parse_mimetype, reify\nfrom .http import HeadersParser\nfrom .payload import (\n    JsonPayload,\n    LookupError,\n    Order,\n    Payload,\n    StringPayload,\n    get_payload,\n    payload_type,\n)\nfrom .streams import StreamReader\n\n__all__ = (\n    \"MultipartReader\",\n    \"MultipartWriter\",\n    \"BodyPartReader\",\n    \"BadContentDispositionHeader\",\n    \"BadContentDispositionParam\",\n    \"parse_content_disposition\",\n    \"content_disposition_filename\",\n)\n\n\nif TYPE_CHECKING:\n    from .client_reqrep import ClientResponse\n\n\nclass BadContentDispositionHeader(RuntimeWarning):\n    pass\n\n\nclass BadContentDispositionParam(RuntimeWarning):\n    pass\n\n\ndef parse_content_disposition(\n    header: Optional[str],\n) -> Tuple[Optional[str], Dict[str, str]]:\n    def is_token(string: str) -> bool:\n        return bool(string) and TOKEN >= set(string)\n\n    def is_quoted(string: str) -> bool:\n        return string[0] == string[-1] == '\"'\n\n    def is_rfc5987(string: str) -> bool:\n        return is_token(string) and string.count(\"'\") == 2\n\n    def is_extended_param(string: str) -> bool:\n        return string.endswith(\"*\")\n\n    def is_continuous_param(string: str) -> bool:\n        pos = string.find(\"*\") + 1\n        if not pos:\n            return False\n        substring = string[pos:-1] if string.endswith(\"*\") else string[pos:]\n        return substring.isdigit()\n\n    def unescape(text: str, *, chars: str = \"\".join(map(re.escape, CHAR))) -> str:\n        return re.sub(f\"\\\\\\\\([{chars}])\", \"\\\\1\", text)\n\n    if not header:\n        return None, {}\n\n    disptype, *parts = header.split(\";\")\n    if not is_token(disptype):\n        warnings.warn(BadContentDispositionHeader(header))\n        return None, {}\n\n    params: Dict[str, str] = {}\n    while parts:\n        item = parts.pop(0)\n\n        if \"=\" not in item:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        key, value = item.split(\"=\", 1)\n        key = key.lower().strip()\n        value = value.lstrip()\n\n        if key in params:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        if not is_token(key):\n            warnings.warn(BadContentDispositionParam(item))\n            continue\n\n        elif is_continuous_param(key):\n            if is_quoted(value):\n                value = unescape(value[1:-1])\n            elif not is_token(value):\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        elif is_extended_param(key):\n            if is_rfc5987(value):\n                encoding, _, value = value.split(\"'\", 2)\n                encoding = encoding or \"utf-8\"\n            else:\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n            try:\n                value = unquote(value, encoding, \"strict\")\n            except UnicodeDecodeError:  # pragma: nocover\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        else:\n            failed = True\n            if is_quoted(value):\n                failed = False\n                value = unescape(value[1:-1].lstrip(\"\\\\/\"))\n            elif is_token(value):\n                failed = False\n            elif parts:\n                # maybe just ; in filename, in any case this is just\n                # one case fix, for proper fix we need to redesign parser\n                _value = f\"{value};{parts[0]}\"\n                if is_quoted(_value):\n                    parts.pop(0)\n                    value = unescape(_value[1:-1].lstrip(\"\\\\/\"))\n                    failed = False\n\n            if failed:\n                warnings.warn(BadContentDispositionHeader(header))\n                return None, {}\n\n        params[key] = value\n\n    return disptype.lower(), params\n\n\ndef content_disposition_filename(\n    params: Mapping[str, str], name: str = \"filename\"\n) -> Optional[str]:\n    name_suf = \"%s*\" % name\n    if not params:\n        return None\n    elif name_suf in params:\n        return params[name_suf]\n    elif name in params:\n        return params[name]\n    else:\n        parts = []\n        fnparams = sorted(\n            (key, value) for key, value in params.items() if key.startswith(name_suf)\n        )\n        for num, (key, value) in enumerate(fnparams):\n            _, tail = key.split(\"*\", 1)\n            if tail.endswith(\"*\"):\n                tail = tail[:-1]\n            if tail == str(num):\n                parts.append(value)\n            else:\n                break\n        if not parts:\n            return None\n        value = \"\".join(parts)\n        if \"'\" in value:\n            encoding, _, value = value.split(\"'\", 2)\n            encoding = encoding or \"utf-8\"\n            return unquote(value, encoding, \"strict\")\n        return value\n\n\nclass MultipartResponseWrapper:\n    \"\"\"Wrapper around the MultipartReader.\n\n    It takes care about\n    underlying connection and close it when it needs in.\n    \"\"\"\n\n    def __init__(\n        self,\n        resp: \"ClientResponse\",\n        stream: \"MultipartReader\",\n    ) -> None:\n        self.resp = resp\n        self.stream = stream\n\n    def __aiter__(self) -> \"MultipartResponseWrapper\":\n        return self\n\n    async def __anext__(\n        self,\n    ) -> Union[\"MultipartReader\", \"BodyPartReader\"]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True when all response data had been read.\"\"\"\n        return self.resp.content.at_eof()\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", \"BodyPartReader\"]]:\n        \"\"\"Emits next multipart reader object.\"\"\"\n        item = await self.stream.next()\n        if self.stream.at_eof():\n            await self.release()\n        return item\n\n    async def release(self) -> None:\n        \"\"\"Release the connection gracefully.\n\n        All remaining content is read to the void.\n        \"\"\"\n        await self.resp.release()\n\n\nclass BodyPartReader:\n    \"\"\"Multipart reader for single body part.\"\"\"\n\n    chunk_size = 8192\n\n    def __init__(\n        self,\n        boundary: bytes,\n        headers: \"CIMultiDictProxy[str]\",\n        content: StreamReader,\n        *,\n        subtype: str = \"mixed\",\n        default_charset: Optional[str] = None,\n    ) -> None:\n        self.headers = headers\n        self._boundary = boundary\n        self._content = content\n        self._default_charset = default_charset\n        self._at_eof = False\n        self._is_form_data = subtype == \"form-data\"\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)\n        self._length = int(length) if length is not None else None\n        self._read_bytes = 0\n        self._unread: Deque[bytes] = deque()\n        self._prev_chunk: Optional[bytes] = None\n        self._content_eof = 0\n        self._cache: Dict[str, Any] = {}\n\n    def __aiter__(self) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(self) -> bytes:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    async def next(self) -> Optional[bytes]:\n        item = await self.read()\n        if not item:\n            return None\n        return item\n\n    async def read(self, *, decode: bool = False) -> bytes:\n        \"\"\"Reads body part data.\n\n        decode: Decodes data following by encoding\n                method from Content-Encoding header. If it missed\n                data remains untouched\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        data = bytearray()\n        while not self._at_eof:\n            data.extend(await self.read_chunk(self.chunk_size))\n        if decode:\n            return self.decode(data)\n        return data\n\n    async def read_chunk(self, size: int = chunk_size) -> bytes:\n        \"\"\"Reads body part content chunk of the specified size.\n\n        size: chunk size\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        if self._length:\n            chunk = await self._read_chunk_from_length(size)\n        else:\n            chunk = await self._read_chunk_from_stream(size)\n\n        self._read_bytes += len(chunk)\n        if self._read_bytes == self._length:\n            self._at_eof = True\n        if self._at_eof:\n            clrf = await self._content.readline()\n            assert (\n                b\"\\r\\n\" == clrf\n            ), \"reader did not read all the data or it is malformed\"\n        return chunk\n\n    async def _read_chunk_from_length(self, size: int) -> bytes:\n        # Reads body part content chunk of the specified size.\n        # The body part must has Content-Length header with proper value.\n        assert self._length is not None, \"Content-Length required for chunked read\"\n        chunk_size = min(size, self._length - self._read_bytes)\n        chunk = await self._content.read(chunk_size)\n        if self._content.at_eof():\n            self._at_eof = True\n        return chunk\n\n    async def _read_chunk_from_stream(self, size: int) -> bytes:\n        # Reads content chunk of body part with unknown length.\n        # The Content-Length header for body part is not necessary.\n        assert (\n            size >= len(self._boundary) + 2\n        ), \"Chunk size must be greater or equal than boundary length + 2\"\n        first_chunk = self._prev_chunk is None\n        if first_chunk:\n            self._prev_chunk = await self._content.read(size)\n\n        chunk = await self._content.read(size)\n        self._content_eof += int(self._content.at_eof())\n        assert self._content_eof < 3, \"Reading after EOF\"\n        assert self._prev_chunk is not None\n        window = self._prev_chunk + chunk\n        sub = b\"\\r\\n\" + self._boundary\n        if first_chunk:\n            idx = window.find(sub)\n        else:\n            idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub)))\n        if idx >= 0:\n            # pushing boundary back to content\n            with warnings.catch_warnings():\n                warnings.filterwarnings(\"ignore\", category=DeprecationWarning)\n                self._content.unread_data(window[idx:])\n            if size > idx:\n                self._prev_chunk = self._prev_chunk[:idx]\n            chunk = window[len(self._prev_chunk) : idx]\n            if not chunk:\n                self._at_eof = True\n        result = self._prev_chunk\n        self._prev_chunk = chunk\n        return result\n\n    async def readline(self) -> bytes:\n        \"\"\"Reads body part by line by line.\"\"\"\n        if self._at_eof:\n            return b\"\"\n\n        if self._unread:\n            line = self._unread.popleft()\n        else:\n            line = await self._content.readline()\n\n        if line.startswith(self._boundary):\n            # the very last boundary may not come with \\r\\n,\n            # so set single rules for everyone\n            sline = line.rstrip(b\"\\r\\n\")\n            boundary = self._boundary\n            last_boundary = self._boundary + b\"--\"\n            # ensure that we read exactly the boundary, not something alike\n            if sline == boundary or sline == last_boundary:\n                self._at_eof = True\n                self._unread.append(line)\n                return b\"\"\n        else:\n            next_line = await self._content.readline()\n            if next_line.startswith(self._boundary):\n                line = line[:-2]  # strip CRLF but only once\n            self._unread.append(next_line)\n\n        return line\n\n    async def release(self) -> None:\n        \"\"\"Like read(), but reads all the data to the void.\"\"\"\n        if self._at_eof:\n            return\n        while not self._at_eof:\n            await self.read_chunk(self.chunk_size)\n\n    async def text(self, *, encoding: Optional[str] = None) -> str:\n        \"\"\"Like read(), but assumes that body part contains text data.\"\"\"\n        data = await self.read(decode=True)\n        # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm\n        # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return data.decode(encoding)\n\n    async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]:\n        \"\"\"Like read(), but assumes that body parts contains JSON data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return None\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return cast(Dict[str, Any], json.loads(data.decode(encoding)))\n\n    async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:\n        \"\"\"Like read(), but assumes that body parts contain form urlencoded data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return []\n        if encoding is not None:\n            real_encoding = encoding\n        else:\n            real_encoding = self.get_charset(default=\"utf-8\")\n        try:\n            decoded_data = data.rstrip().decode(real_encoding)\n        except UnicodeDecodeError:\n            raise ValueError(\"data cannot be decoded with %s encoding\" % real_encoding)\n\n        return parse_qsl(\n            decoded_data,\n            keep_blank_values=True,\n            encoding=real_encoding,\n        )\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the boundary was reached or False otherwise.\"\"\"\n        return self._at_eof\n\n    def decode(self, data: bytes) -> bytes:\n        \"\"\"Decodes data.\n\n        Decoding is done according the specified Content-Encoding\n        or Content-Transfer-Encoding headers value.\n        \"\"\"\n        if CONTENT_TRANSFER_ENCODING in self.headers:\n            data = self._decode_content_transfer(data)\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        if not self._is_form_data and CONTENT_ENCODING in self.headers:\n            return self._decode_content(data)\n        return data\n\n    def _decode_content(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_ENCODING, \"\").lower()\n        if encoding == \"identity\":\n            return data\n        if encoding in {\"deflate\", \"gzip\"}:\n            return ZLibDecompressor(\n                encoding=encoding,\n                suppress_deflate_header=True,\n            ).decompress_sync(data)\n\n        raise RuntimeError(f\"unknown content encoding: {encoding}\")\n\n    def _decode_content_transfer(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n\n        if encoding == \"base64\":\n            return base64.b64decode(data)\n        elif encoding == \"quoted-printable\":\n            return binascii.a2b_qp(data)\n        elif encoding in (\"binary\", \"8bit\", \"7bit\"):\n            return data\n        else:\n            raise RuntimeError(\n                \"unknown content transfer encoding: {}\" \"\".format(encoding)\n            )\n\n    def get_charset(self, default: str) -> str:\n        \"\"\"Returns charset parameter from Content-Type header or default.\"\"\"\n        ctype = self.headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n        return mimetype.parameters.get(\"charset\", self._default_charset or default)\n\n    @reify\n    def name(self) -> Optional[str]:\n        \"\"\"Returns name specified in Content-Disposition header.\n\n        If the header is missing or malformed, returns None.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"name\")\n\n    @reify\n    def filename(self) -> Optional[str]:\n        \"\"\"Returns filename specified in Content-Disposition header.\n\n        Returns None if the header is missing or malformed.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"filename\")\n\n\n@payload_type(BodyPartReader, order=Order.try_first)\nclass BodyPartReaderPayload(Payload):\n    def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:\n        super().__init__(value, *args, **kwargs)\n\n        params: Dict[str, str] = {}\n        if value.name is not None:\n            params[\"name\"] = value.name\n        if value.filename is not None:\n            params[\"filename\"] = value.filename\n\n        if params:\n            self.set_content_disposition(\"attachment\", True, **params)\n\n    async def write(self, writer: Any) -> None:\n        field = self._value\n        chunk = await field.read_chunk(size=2**16)\n        while chunk:\n            await writer.write(field.decode(chunk))\n            chunk = await field.read_chunk(size=2**16)\n\n\nclass MultipartReader:\n    \"\"\"Multipart body reader.\"\"\"\n\n    #: Response wrapper, used when multipart readers constructs from response.\n    response_wrapper_cls = MultipartResponseWrapper\n    #: Multipart reader class, used to handle multipart/* body parts.\n    #: None points to type(self)\n    multipart_reader_cls = None\n    #: Body part reader class for non multipart/* content types.\n    part_reader_cls = BodyPartReader\n\n    def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None:\n        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])\n        assert self._mimetype.type == \"multipart\", \"multipart/* content type expected\"\n        if \"boundary\" not in self._mimetype.parameters:\n            raise ValueError(\n                \"boundary missed for Content-Type: %s\" % headers[CONTENT_TYPE]\n            )\n\n        self.headers = headers\n        self._boundary = (\"--\" + self._get_boundary()).encode()\n        self._content = content\n        self._default_charset: Optional[str] = None\n        self._last_part: Optional[Union[\"MultipartReader\", BodyPartReader]] = None\n        self._at_eof = False\n        self._at_bof = True\n        self._unread: List[bytes] = []\n\n    def __aiter__(\n        self,\n    ) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    @classmethod\n    def from_response(\n        cls,\n        response: \"ClientResponse\",\n    ) -> MultipartResponseWrapper:\n        \"\"\"Constructs reader instance from HTTP response.\n\n        :param response: :class:`~aiohttp.client.ClientResponse` instance\n        \"\"\"\n        obj = cls.response_wrapper_cls(\n            response, cls(response.headers, response.content)\n        )\n        return obj\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the final boundary was reached, false otherwise.\"\"\"\n        return self._at_eof\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        \"\"\"Emits the next multipart body part.\"\"\"\n        # So, if we're at BOF, we need to skip till the boundary.\n        if self._at_eof:\n            return None\n        await self._maybe_release_last_part()\n        if self._at_bof:\n            await self._read_until_first_boundary()\n            self._at_bof = False\n        else:\n            await self._read_boundary()\n        if self._at_eof:  # we just read the last boundary, nothing to do there\n            return None\n\n        part = await self.fetch_next_part()\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6\n        if (\n            self._last_part is None\n            and self._mimetype.subtype == \"form-data\"\n            and isinstance(part, BodyPartReader)\n        ):\n            _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION))\n            if params.get(\"name\") == \"_charset_\":\n                # Longest encoding in https://encoding.spec.whatwg.org/encodings.json\n                # is 19 characters, so 32 should be more than enough for any valid encoding.\n                charset = await part.read_chunk(32)\n                if len(charset) > 31:\n                    raise RuntimeError(\"Invalid default charset\")\n                self._default_charset = charset.strip().decode()\n                part = await self.fetch_next_part()\n        self._last_part = part\n        return self._last_part\n\n    async def release(self) -> None:\n        \"\"\"Reads all the body parts to the void till the final boundary.\"\"\"\n        while not self._at_eof:\n            item = await self.next()\n            if item is None:\n                break\n            await item.release()\n\n    async def fetch_next_part(\n        self,\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Returns the next body part reader.\"\"\"\n        headers = await self._read_headers()\n        return self._get_part_reader(headers)\n\n    def _get_part_reader(\n        self,\n        headers: \"CIMultiDictProxy[str]\",\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Dispatches the response by the `Content-Type` header.\n\n        Returns a suitable reader instance.\n\n        :param dict headers: Response headers\n        \"\"\"\n        ctype = headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n\n        if mimetype.type == \"multipart\":\n            if self.multipart_reader_cls is None:\n                return type(self)(headers, self._content)\n            return self.multipart_reader_cls(headers, self._content)\n        else:\n            return self.part_reader_cls(\n                self._boundary,\n                headers,\n                self._content,\n                subtype=self._mimetype.subtype,\n                default_charset=self._default_charset,\n            )\n\n    def _get_boundary(self) -> str:\n        boundary = self._mimetype.parameters[\"boundary\"]\n        if len(boundary) > 70:\n            raise ValueError(\"boundary %r is too long (70 chars max)\" % boundary)\n\n        return boundary\n\n    async def _readline(self) -> bytes:\n        if self._unread:\n            return self._unread.pop()\n        return await self._content.readline()\n\n    async def _read_until_first_boundary(self) -> None:\n        while True:\n            chunk = await self._readline()\n            if chunk == b\"\":\n                raise ValueError(\n                    \"Could not find starting boundary %r\" % (self._boundary)\n                )\n            chunk = chunk.rstrip()\n            if chunk == self._boundary:\n                return\n            elif chunk == self._boundary + b\"--\":\n                self._at_eof = True\n                return\n\n    async def _read_boundary(self) -> None:\n        chunk = (await self._readline()).rstrip()\n        if chunk == self._boundary:\n            pass\n        elif chunk == self._boundary + b\"--\":\n            self._at_eof = True\n            epilogue = await self._readline()\n            next_line = await self._readline()\n\n            # the epilogue is expected and then either the end of input or the\n            # parent multipart boundary, if the parent boundary is found then\n            # it should be marked as unread and handed to the parent for\n            # processing\n            if next_line[:2] == b\"--\":\n                self._unread.append(next_line)\n            # otherwise the request is likely missing an epilogue and both\n            # lines should be passed to the parent for processing\n            # (this handles the old behavior gracefully)\n            else:\n                self._unread.extend([next_line, epilogue])\n        else:\n            raise ValueError(f\"Invalid boundary {chunk!r}, expected {self._boundary!r}\")\n\n    async def _read_headers(self) -> \"CIMultiDictProxy[str]\":\n        lines = [b\"\"]\n        while True:\n            chunk = await self._content.readline()\n            chunk = chunk.strip()\n            lines.append(chunk)\n            if not chunk:\n                break\n        parser = HeadersParser()\n        headers, raw_headers = parser.parse_headers(lines)\n        return headers\n\n    async def _maybe_release_last_part(self) -> None:\n        \"\"\"Ensures that the last read body part is read completely.\"\"\"\n        if self._last_part is not None:\n            if not self._last_part.at_eof():\n                await self._last_part.release()\n            self._unread.extend(self._last_part._unread)\n            self._last_part = None\n\n\n_Part = Tuple[Payload, str, str]\n\n\nclass MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            if self._is_form_data:\n                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n                assert CONTENT_DISPOSITION in part.headers\n                assert \"name=\" in part.headers[CONTENT_DISPOSITION]\n\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")\n\n\nclass MultipartPayloadWriter:\n    def __init__(self, writer: Any) -> None:\n        self._writer = writer\n        self._encoding: Optional[str] = None\n        self._compress: Optional[ZLibCompressor] = None\n        self._encoding_buffer: Optional[bytearray] = None\n\n    def enable_encoding(self, encoding: str) -> None:\n        if encoding == \"base64\":\n            self._encoding = encoding\n            self._encoding_buffer = bytearray()\n        elif encoding == \"quoted-printable\":\n            self._encoding = \"quoted-printable\"\n\n    def enable_compression(\n        self, encoding: str = \"deflate\", strategy: int = zlib.Z_DEFAULT_STRATEGY\n    ) -> None:\n        self._compress = ZLibCompressor(\n            encoding=encoding,\n            suppress_deflate_header=True,\n            strategy=strategy,\n        )\n\n    async def write_eof(self) -> None:\n        if self._compress is not None:\n            chunk = self._compress.flush()\n            if chunk:\n                self._compress = None\n                await self.write(chunk)\n\n        if self._encoding == \"base64\":\n            if self._encoding_buffer:\n                await self._writer.write(base64.b64encode(self._encoding_buffer))\n\n    async def write(self, chunk: bytes) -> None:\n        if self._compress is not None:\n            if chunk:\n                chunk = await self._compress.compress(chunk)\n                if not chunk:\n                    return\n\n        if self._encoding == \"base64\":\n            buf = self._encoding_buffer\n            assert buf is not None\n            buf.extend(chunk)\n\n            if buf:\n                div, mod = divmod(len(buf), 3)\n                enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :])\n                if enc_chunk:\n                    b64chunk = base64.b64encode(enc_chunk)\n                    await self._writer.write(b64chunk)\n        elif self._encoding == \"quoted-printable\":\n            await self._writer.write(binascii.b2a_qp(chunk))\n        else:\n            await self._writer.write(chunk)\n",
            "header": {
                "file": {
                    "path": "aiohttp/multipart.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "a43ec545",
                "new_commit_hash": "fcdf1618",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/multipart.py"
            },
            "plus_file": {
                "path": "b/aiohttp/multipart.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 848,
                            "length": 8
                        },
                        "new_range": {
                            "start": 848,
                            "length": 6
                        },
                        "section": "class MultipartWriter(Payload):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        if self._is_form_data:",
                                "type": "context"
                            },
                            {
                                "content": "            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7",
                                "type": "context"
                            },
                            {
                                "content": "            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8",
                                "type": "context"
                            },
                            {
                                "content": "            assert CONTENT_DISPOSITION in payload.headers",
                                "type": "deleted"
                            },
                            {
                                "content": "            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]",
                                "type": "deleted"
                            },
                            {
                                "content": "            assert (",
                                "type": "context"
                            },
                            {
                                "content": "                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}",
                                "type": "context"
                            },
                            {
                                "content": "                & payload.headers.keys()",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/multipart.py",
                            "type": "method",
                            "name": "MultipartWriter.append_payload",
                            "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert CONTENT_DISPOSITION in payload.headers\n            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 844,
                            "end_lineno": 878,
                            "parent": {
                                "file_name": "aiohttp/multipart.py",
                                "type": "class",
                                "name": "MultipartWriter",
                                "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert CONTENT_DISPOSITION in payload.headers\n            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 749,
                                "end_lineno": 950,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "aiohttp/multipart.py",
                            "type": "class",
                            "name": "MultipartWriter",
                            "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert CONTENT_DISPOSITION in payload.headers\n            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 749,
                            "end_lineno": 950,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 930,
                            "length": 6
                        },
                        "new_range": {
                            "start": 928,
                            "length": 11
                        },
                        "section": "class MultipartWriter(Payload):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    async def write(self, writer: Any, close_boundary: bool = True) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "        \"\"\"Write body.\"\"\"",
                                "type": "context"
                            },
                            {
                                "content": "        for part, encoding, te_encoding in self._parts:",
                                "type": "context"
                            },
                            {
                                "content": "            if self._is_form_data:",
                                "type": "added"
                            },
                            {
                                "content": "                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2",
                                "type": "added"
                            },
                            {
                                "content": "                assert CONTENT_DISPOSITION in part.headers",
                                "type": "added"
                            },
                            {
                                "content": "                assert \"name=\" in part.headers[CONTENT_DISPOSITION]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")",
                                "type": "context"
                            },
                            {
                                "content": "            await writer.write(part._binary_headers)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/multipart.py",
                            "type": "class",
                            "name": "MultipartWriter",
                            "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            if self._is_form_data:\n                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n                assert CONTENT_DISPOSITION in part.headers\n                assert \"name=\" in part.headers[CONTENT_DISPOSITION]\n\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 749,
                            "end_lineno": 953,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/multipart.py",
                            "type": "method",
                            "name": "MultipartWriter.write",
                            "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            if self._is_form_data:\n                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n                assert CONTENT_DISPOSITION in part.headers\n                assert \"name=\" in part.headers[CONTENT_DISPOSITION]\n\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 928,
                            "end_lineno": 953,
                            "parent": {
                                "file_name": "aiohttp/multipart.py",
                                "type": "class",
                                "name": "MultipartWriter",
                                "content": "class MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            if self._is_form_data:\n                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2\n                assert CONTENT_DISPOSITION in part.headers\n                assert \"name=\" in part.headers[CONTENT_DISPOSITION]\n\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 749,
                                "end_lineno": 953,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport io\nimport json\nimport pathlib\nimport zlib\nfrom unittest import mock\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import payload\nfrom aiohttp.hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom aiohttp.helpers import parse_mimetype\nfrom aiohttp.multipart import MultipartResponseWrapper\nfrom aiohttp.streams import StreamReader\nfrom aiohttp.test_utils import make_mocked_coro\n\nBOUNDARY = b\"--:\"\n\n\nnewline = b\"\\r\\n\"\n\n\n@pytest.fixture\ndef buf():\n    return bytearray()\n\n\n@pytest.fixture\ndef stream(buf):\n    writer = mock.Mock()\n\n    async def write(chunk):\n        buf.extend(chunk)\n\n    writer.write.side_effect = write\n    return writer\n\n\n@pytest.fixture\ndef writer():\n    return aiohttp.MultipartWriter(boundary=\":\")\n\n\nclass Response:\n    def __init__(self, headers, content):\n        self.headers = headers\n        self.content = content\n\n\nclass Stream:\n    def __init__(self, content):\n        self.content = io.BytesIO(content)\n\n    async def read(self, size=None):\n        return self.content.read(size)\n\n    def at_eof(self):\n        return self.content.tell() == len(self.content.getbuffer())\n\n    async def readline(self):\n        return self.content.readline()\n\n    def unread_data(self, data):\n        self.content = io.BytesIO(data + self.content.read())\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.content.close()\n\n\nclass StreamWithShortenRead(Stream):\n    def __init__(self, content):\n        self._first = True\n        super().__init__(content)\n\n    async def read(self, size=None):\n        if size is not None and self._first:\n            self._first = False\n            size = size // 2\n        return await super().read(size)\n\n\nclass TestMultipartResponseWrapper:\n    def test_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.at_eof()\n        assert wrapper.resp.content.at_eof.called\n\n    async def test_next(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = False\n        await wrapper.next()\n        assert wrapper.stream.next.called\n\n    async def test_release(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        await wrapper.release()\n        assert wrapper.resp.release.called\n\n    async def test_release_when_stream_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = True\n        await wrapper.next()\n        assert wrapper.stream.next.called\n        assert wrapper.resp.release.called\n\n\nclass TestPartReader:\n    async def test_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_next_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n            result = await obj.next()\n            assert result is None\n\n    async def test_read(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_at_eof(self) -> None:\n        with Stream(b\"--:\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            obj._at_eof = True\n            result = await obj.read_chunk()\n        assert b\"\" == result\n\n    async def test_read_chunk_without_content_length(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            c1 = await obj.read_chunk(8)\n            c2 = await obj.read_chunk(8)\n            c3 = await obj.read_chunk(8)\n        assert c1 + c2 == b\"Hello, world!\"\n        assert c3 == b\"\"\n\n    async def test_read_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, \"),\n                    prepare(b\"World\"),\n                    prepare(b\"!%s--:\" % newline),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(8)\n                assert c1 == b\"Hello, \"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"World\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"!\"\n\n    async def test_read_all_at_once(self) -> None:\n        data = b\"Hello, World!%s--:--%s\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk()\n            assert b\"Hello, World!\" == result\n            result = await obj.read_chunk()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_incomplete_body_chunked(self) -> None:\n        data = b\"Hello, World!%s--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = b\"\"\n            with pytest.raises(AssertionError):\n                for _ in range(4):\n                    result += await obj.read_chunk(7)\n        assert data == result\n\n    async def test_read_boundary_with_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, World\"),\n                    prepare(b\"!%s\" % newline),\n                    prepare(b\"--:\"),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(12)\n                assert c1 == b\"Hello, World\"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"!\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"\"\n\n    async def test_multi_read_chunk(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk(8)\n            assert b\"Hello,\" == result\n            result = await obj.read_chunk(8)\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_properly_counts_read_bytes(self) -> None:\n        expected = b\".\" * 10\n        tail = b\"%s--:--\" % newline\n        size = len(expected)\n        with StreamWithShortenRead(expected + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": size},\n                stream,\n            )\n            result = bytearray()\n            while True:\n                chunk = await obj.read_chunk()\n                if not chunk:\n                    break\n                result.extend(chunk)\n        assert size == len(result)\n        assert b\".\" * size == result\n        assert obj.at_eof()\n\n    async def test_read_does_not_read_boundary(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert b\"--:\" == (await stream.read())\n\n    async def test_multiread(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello,\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_multiline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello\\n,\\r\\nworld!\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_respects_content_length(self) -> None:\n        data = b\".\" * 100500\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.read()\n            assert data == result\n            assert obj.at_eof()\n\n    async def test_read_with_content_encoding_gzip(self) -> None:\n        with Stream(\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n            b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"gzip\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_deflate(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_identity(self) -> None:\n        thing = (\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n        )\n        with Stream(thing + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"identity\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert thing == result\n\n    async def test_read_with_content_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"snappy\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_with_content_transfer_encoding_base64(self) -> None:\n        with Stream(b\"VGltZSB0byBSZWxheCE=%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"base64\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_transfer_encoding_quoted_printable(self) -> None:\n        with Stream(\n            b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n            b\" =D0=BC=D0=B8=D1=80!%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        expected = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        assert result == expected\n\n    @pytest.mark.parametrize(\"encoding\", (\"binary\", \"8bit\", \"7bit\"))\n    async def test_read_with_content_transfer_encoding_binary(\n        self, encoding: str\n    ) -> None:\n        data = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        with Stream(data + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: encoding},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert data == result\n\n    async def test_read_with_content_transfer_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"unknown\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_text(self) -> None:\n        with Stream(b\"Hello, world!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Hello, world!\" == result\n\n    async def test_read_text_default_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"utf-8\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text(encoding=\"cp1251\")\n        assert data == result\n\n    async def test_read_text_guess_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"text/plain;charset=cp1251\"},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_compressed(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\" b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"text/plain\"},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Time to Relax!\" == result\n\n    async def test_read_text_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {CONTENT_TYPE: \"text/plain\"}, stream)\n            obj._at_eof = True\n            result = await obj.text()\n        assert \"\" == result\n\n    async def test_read_json(self) -> None:\n        with Stream(b'{\"test\": \"passed\"}%s--:--' % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json(encoding=\"cp1251\")\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_guess_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json; charset=cp1251\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_compressed(self) -> None:\n        with Stream(\n            b\"\\xabV*I-.Q\\xb2RP*H,.NMQ\\xaa\\x05\\x00\" b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY, {CONTENT_TYPE: \"application/json\"}, stream\n            )\n            obj._at_eof = True\n            result = await obj.json()\n        assert result is None\n\n    async def test_read_form(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_invalid_utf8(self) -> None:\n        invalid_unicode_byte = b\"\\xff\"\n        data = invalid_unicode_byte + b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            with pytest.raises(\n                ValueError, match=\"data cannot be decoded with utf-8 encoding\"\n            ):\n                await obj.form()\n\n    async def test_read_form_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form(encoding=\"cp1251\")\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_guess_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded; charset=utf-8\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            obj._at_eof = True\n            result = await obj.form()\n        assert not result\n\n    async def test_readline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.readline()\n            assert b\"Hello\\n\" == result\n            result = await obj.readline()\n            assert b\",\\r\\n\" == result\n            result = await obj.readline()\n            assert b\"world!\" == result\n            result = await obj.readline()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n            await obj.release()\n            assert obj.at_eof()\n            assert remained == stream.content.read()\n\n    async def test_release_respects_content_length(self) -> None:\n        with Stream(b\".\" * 100500 + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.release()\n            assert result is None\n            assert obj.at_eof()\n\n    async def test_release_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            await obj.release()\n            await obj.release()\n            assert remained == stream.content.read()\n\n    async def test_filename(self) -> None:\n        part = aiohttp.BodyPartReader(\n            BOUNDARY, {CONTENT_DISPOSITION: \"attachment; filename=foo.html\"}, None\n        )\n        assert \"foo.html\" == part.filename\n\n    async def test_reading_long_part(self) -> None:\n        size = 2 * 2**16\n        protocol = mock.Mock(_reading_paused=False)\n        stream = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop())\n        stream.feed_data(b\"0\" * size + b\"\\r\\n--:--\")\n        stream.feed_eof()\n        obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n        data = await obj.read()\n        assert len(data) == size\n\n\nclass TestMultipartReader:\n    def test_from_response(self) -> None:\n        with Stream(b\"--:%s\\r\\nhello%s--:--\" % (newline, newline)) as stream:\n            resp = Response(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = aiohttp.MultipartReader.from_response(resp)\n        assert isinstance(res, MultipartResponseWrapper)\n        assert isinstance(res.stream, aiohttp.MultipartReader)\n\n    def test_bad_boundary(self) -> None:\n        with Stream(b\"\") as stream:\n            resp = Response(\n                {CONTENT_TYPE: \"multipart/related;boundary=\" + \"a\" * 80}, stream\n            )\n            with pytest.raises(ValueError):\n                aiohttp.MultipartReader.from_response(resp)\n\n    def test_dispatch(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_bodypart(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_multipart(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\" b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader(\n                {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n            )\n        assert isinstance(res, reader.__class__)\n\n    def test_dispatch_custom_multipart_reader(self) -> None:\n        class CustomReader(aiohttp.MultipartReader):\n            pass\n\n        reader = aiohttp.MultipartReader(\n            {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n            Stream(\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"test\\r\\n\"\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"passed\\r\\n\"\n                b\"----:----\\r\\n\"\n                b\"--:--\"\n            ),\n        )\n        reader.multipart_reader_cls = CustomReader\n        res = reader._get_part_reader(\n            {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n        )\n        assert isinstance(res, CustomReader)\n\n    async def test_emit_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = await reader.next()\n        assert isinstance(res, reader.part_reader_cls)\n\n    async def test_invalid_boundary(self) -> None:\n        with Stream(b\"---:%s\\r\\necho%s---:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(ValueError):\n                await reader.next()\n\n    async def test_release(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Type: multipart/related;boundary=--:--\",\n                    b\"\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\",\n                    b\"\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/mixed;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_release(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            res = await reader.next()\n            assert res is None\n\n    async def test_second_next_releases_previous_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_release_without_read_the_last_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            second = await reader.next()\n            third = await reader.next()\n\n            assert first.at_eof()\n            assert second.at_eof()\n            assert second.at_eof()\n            assert third is None\n\n    async def test_read_chunk_by_length_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Length: 4\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"Content-Length: 6\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    read_part += await part.read_chunk(3)\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"test\", b\"passed\"]\n\n    async def test_read_chunk_from_stream_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"chunk\",\n                    b\"--:\",\n                    b\"\",\n                    b\"two_chunks\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    chunk = await part.read_chunk(5)\n                    assert chunk\n                    read_part += chunk\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"chunk\", b\"two_chunks\"]\n\n    async def test_reading_skips_prelude(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"Multi-part data is not supported.\",\n                    b\"\",\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_read_form_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"ascii\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain;charset=UTF-8\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field2\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field3\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            field1 = await reader.next()\n            assert field1.name == \"field1\"\n            assert field1.get_charset(\"default\") == \"ascii\"\n            field2 = await reader.next()\n            assert field2.name == \"field2\"\n            assert field2.get_charset(\"default\") == \"UTF-8\"\n            field3 = await reader.next()\n            assert field3.name == \"field3\"\n            assert field3.get_charset(\"default\") == \"ascii\"\n\n    async def test_read_form_invalid_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"this-value-is-too-long-to-be-a-charset\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(RuntimeError, match=\"Invalid default charset\"):\n                await reader.next()\n\n\nasync def test_writer(writer) -> None:\n    assert writer.size == 7\n    assert writer.boundary == \":\"\n\n\nasync def test_writer_serialize_io_chunk(buf, stream, writer) -> None:\n    with io.BytesIO(b\"foobarbaz\") as file_handle:\n        writer.append(file_handle)\n        await writer.write(stream)\n    assert (\n        buf == b\"--:\\r\\nContent-Type: application/octet-stream\"\n        b\"\\r\\nContent-Length: 9\\r\\n\\r\\nfoobarbaz\\r\\n--:--\\r\\n\"\n    )\n\n\nasync def test_writer_serialize_json(buf, stream, writer) -> None:\n    writer.append_json({\"привет\": \"мир\"})\n    await writer.write(stream)\n    assert (\n        b'{\"\\\\u043f\\\\u0440\\\\u0438\\\\u0432\\\\u0435\\\\u0442\":'\n        b' \"\\\\u043c\\\\u0438\\\\u0440\"}' in buf\n    )\n\n\nasync def test_writer_serialize_form(buf, stream, writer) -> None:\n    data = [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"zoo\")]\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"foo=bar&foo=baz&boo=zoo\" in buf\n\n\nasync def test_writer_serialize_form_dict(buf, stream, writer) -> None:\n    data = {\"hello\": \"мир\"}\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"hello=%D0%BC%D0%B8%D1%80\" in buf\n\n\nasync def test_writer_write(buf, stream, writer) -> None:\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n\n    sub_multipart = aiohttp.MultipartWriter(boundary=\"::\")\n    sub_multipart.append(\"nested content\")\n    sub_multipart.headers[\"X-CUSTOM\"] = \"test\"\n    writer.append(sub_multipart)\n    await writer.write(stream)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b'Content-Type: multipart/mixed; boundary=\"::\"\\r\\n'\n        b\"X-CUSTOM: test\\r\\nContent-Length: 93\\r\\n\\r\\n\"\n        b\"--::\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 14\\r\\n\\r\\n\"\n        b\"nested content\\r\\n\"\n        b\"--::--\\r\\n\"\n        b\"\\r\\n\"\n        b\"--:--\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_close_boundary(buf, stream) -> None:\n    writer = aiohttp.MultipartWriter(boundary=\":\")\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n    await writer.write(stream, close_boundary=False)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_parts(buf, stream, writer) -> None:\n    await writer.write(stream)\n    assert b\"--:--\\r\\n\" == bytes(buf)\n\n\nasync def test_writer_serialize_with_content_encoding_gzip(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"gzip\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: gzip\" == headers\n    )\n\n    decompressor = zlib.decompressobj(wbits=16 + zlib.MAX_WBITS)\n    data = decompressor.decompress(message.split(b\"\\r\\n\")[0])\n    data += decompressor.flush()\n    assert b\"Time to Relax!\" == data\n\n\nasync def test_writer_serialize_with_content_encoding_deflate(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"deflate\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: deflate\" == headers\n    )\n\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\\r\\n--:--\\r\\n\"\n    assert thing == message\n\n\nasync def test_writer_serialize_with_content_encoding_identity(buf, stream, writer):\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n    writer.append(thing, {CONTENT_ENCODING: \"identity\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: application/octet-stream\\r\\n\"\n        b\"Content-Encoding: identity\\r\\n\"\n        b\"Content-Length: 16\" == headers\n    )\n\n    assert thing == message.split(b\"\\r\\n\")[0]\n\n\ndef test_writer_serialize_with_content_encoding_unknown(buf, stream, writer):\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"snappy\"})\n\n\nasync def test_writer_with_content_transfer_encoding_base64(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"base64\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: base64\" == headers\n    )\n\n    assert b\"VGltZSB0byBSZWxheCE=\" == message.split(b\"\\r\\n\")[0]\n\n\nasync def test_writer_content_transfer_encoding_quote_printable(buf, stream, writer):\n    writer.append(\"Привет, мир!\", {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: quoted-printable\" == headers\n    )\n\n    assert (\n        b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n        b\" =D0=BC=D0=B8=D1=80!\" == message.split(b\"\\r\\n\")[0]\n    )\n\n\ndef test_writer_content_transfer_encoding_unknown(buf, stream, writer) -> None:\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"unknown\"})\n\n\nclass TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )\n\n\nasync def test_async_for_reader() -> None:\n    data = [{\"test\": \"passed\"}, 42, b\"plain text\", b\"aiohttp\\n\", b\"no epilogue\"]\n    with Stream(\n        b\"\\r\\n\".join(\n            [\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[0]).encode(),\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[1]).encode(),\n                b\"--:\",\n                b'Content-Type: multipart/related; boundary=\"::\"',\n                b\"\",\n                b\"--::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[2],\n                b\"--::\",\n                b'Content-Disposition: attachment; filename=\"aiohttp\"',\n                b\"Content-Type: text/plain\",\n                b\"Content-Length: 28\",\n                b\"Content-Encoding: gzip\",\n                b\"\",\n                b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03K\\xcc\\xcc\\xcf())\"\n                b\"\\xe0\\x02\\x00\\xd6\\x90\\xe2O\\x08\\x00\\x00\\x00\",\n                b\"--::\",\n                b'Content-Type: multipart/related; boundary=\":::\"',\n                b\"\",\n                b\"--:::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[4],\n                b\"--:::--\",\n                b\"--::--\",\n                b\"\",\n                b\"--:--\",\n                b\"\",\n            ]\n        )\n    ) as stream:\n        reader = aiohttp.MultipartReader(\n            headers={CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'},\n            content=stream,\n        )\n        idata = iter(data)\n\n        async def check(reader):\n            async for part in reader:\n                if isinstance(part, aiohttp.BodyPartReader):\n                    if part.headers[CONTENT_TYPE] == \"application/json\":\n                        assert next(idata) == (await part.json())\n                    else:\n                        assert next(idata) == await part.read(decode=True)\n                else:\n                    await check(part)\n\n        await check(reader)\n\n\nasync def test_async_for_bodypart() -> None:\n    with Stream(b\"foobarbaz\\r\\n--:--\") as stream:\n        part = aiohttp.BodyPartReader(boundary=b\"--:\", headers={}, content=stream)\n        async for data in part:\n            assert data == b\"foobarbaz\"\n",
            "new_file_content": "import asyncio\nimport io\nimport json\nimport pathlib\nimport zlib\nfrom unittest import mock\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import payload\nfrom aiohttp.hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom aiohttp.helpers import parse_mimetype\nfrom aiohttp.multipart import MultipartResponseWrapper\nfrom aiohttp.streams import StreamReader\nfrom aiohttp.test_utils import make_mocked_coro\n\nBOUNDARY = b\"--:\"\n\n\nnewline = b\"\\r\\n\"\n\n\n@pytest.fixture\ndef buf():\n    return bytearray()\n\n\n@pytest.fixture\ndef stream(buf):\n    writer = mock.Mock()\n\n    async def write(chunk):\n        buf.extend(chunk)\n\n    writer.write.side_effect = write\n    return writer\n\n\n@pytest.fixture\ndef writer():\n    return aiohttp.MultipartWriter(boundary=\":\")\n\n\nclass Response:\n    def __init__(self, headers, content):\n        self.headers = headers\n        self.content = content\n\n\nclass Stream:\n    def __init__(self, content):\n        self.content = io.BytesIO(content)\n\n    async def read(self, size=None):\n        return self.content.read(size)\n\n    def at_eof(self):\n        return self.content.tell() == len(self.content.getbuffer())\n\n    async def readline(self):\n        return self.content.readline()\n\n    def unread_data(self, data):\n        self.content = io.BytesIO(data + self.content.read())\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.content.close()\n\n\nclass StreamWithShortenRead(Stream):\n    def __init__(self, content):\n        self._first = True\n        super().__init__(content)\n\n    async def read(self, size=None):\n        if size is not None and self._first:\n            self._first = False\n            size = size // 2\n        return await super().read(size)\n\n\nclass TestMultipartResponseWrapper:\n    def test_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.at_eof()\n        assert wrapper.resp.content.at_eof.called\n\n    async def test_next(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = False\n        await wrapper.next()\n        assert wrapper.stream.next.called\n\n    async def test_release(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        await wrapper.release()\n        assert wrapper.resp.release.called\n\n    async def test_release_when_stream_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = True\n        await wrapper.next()\n        assert wrapper.stream.next.called\n        assert wrapper.resp.release.called\n\n\nclass TestPartReader:\n    async def test_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_next_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n            result = await obj.next()\n            assert result is None\n\n    async def test_read(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_at_eof(self) -> None:\n        with Stream(b\"--:\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            obj._at_eof = True\n            result = await obj.read_chunk()\n        assert b\"\" == result\n\n    async def test_read_chunk_without_content_length(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            c1 = await obj.read_chunk(8)\n            c2 = await obj.read_chunk(8)\n            c3 = await obj.read_chunk(8)\n        assert c1 + c2 == b\"Hello, world!\"\n        assert c3 == b\"\"\n\n    async def test_read_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, \"),\n                    prepare(b\"World\"),\n                    prepare(b\"!%s--:\" % newline),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(8)\n                assert c1 == b\"Hello, \"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"World\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"!\"\n\n    async def test_read_all_at_once(self) -> None:\n        data = b\"Hello, World!%s--:--%s\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk()\n            assert b\"Hello, World!\" == result\n            result = await obj.read_chunk()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_incomplete_body_chunked(self) -> None:\n        data = b\"Hello, World!%s--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = b\"\"\n            with pytest.raises(AssertionError):\n                for _ in range(4):\n                    result += await obj.read_chunk(7)\n        assert data == result\n\n    async def test_read_boundary_with_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, World\"),\n                    prepare(b\"!%s\" % newline),\n                    prepare(b\"--:\"),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(12)\n                assert c1 == b\"Hello, World\"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"!\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"\"\n\n    async def test_multi_read_chunk(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk(8)\n            assert b\"Hello,\" == result\n            result = await obj.read_chunk(8)\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_properly_counts_read_bytes(self) -> None:\n        expected = b\".\" * 10\n        tail = b\"%s--:--\" % newline\n        size = len(expected)\n        with StreamWithShortenRead(expected + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": size},\n                stream,\n            )\n            result = bytearray()\n            while True:\n                chunk = await obj.read_chunk()\n                if not chunk:\n                    break\n                result.extend(chunk)\n        assert size == len(result)\n        assert b\".\" * size == result\n        assert obj.at_eof()\n\n    async def test_read_does_not_read_boundary(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert b\"--:\" == (await stream.read())\n\n    async def test_multiread(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello,\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_multiline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello\\n,\\r\\nworld!\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_respects_content_length(self) -> None:\n        data = b\".\" * 100500\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.read()\n            assert data == result\n            assert obj.at_eof()\n\n    async def test_read_with_content_encoding_gzip(self) -> None:\n        with Stream(\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n            b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"gzip\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_deflate(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_identity(self) -> None:\n        thing = (\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n        )\n        with Stream(thing + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"identity\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert thing == result\n\n    async def test_read_with_content_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"snappy\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_with_content_transfer_encoding_base64(self) -> None:\n        with Stream(b\"VGltZSB0byBSZWxheCE=%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"base64\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_transfer_encoding_quoted_printable(self) -> None:\n        with Stream(\n            b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n            b\" =D0=BC=D0=B8=D1=80!%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        expected = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        assert result == expected\n\n    @pytest.mark.parametrize(\"encoding\", (\"binary\", \"8bit\", \"7bit\"))\n    async def test_read_with_content_transfer_encoding_binary(\n        self, encoding: str\n    ) -> None:\n        data = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        with Stream(data + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: encoding},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert data == result\n\n    async def test_read_with_content_transfer_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"unknown\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_text(self) -> None:\n        with Stream(b\"Hello, world!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Hello, world!\" == result\n\n    async def test_read_text_default_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"utf-8\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text(encoding=\"cp1251\")\n        assert data == result\n\n    async def test_read_text_guess_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"text/plain;charset=cp1251\"},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_compressed(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\" b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"text/plain\"},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Time to Relax!\" == result\n\n    async def test_read_text_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {CONTENT_TYPE: \"text/plain\"}, stream)\n            obj._at_eof = True\n            result = await obj.text()\n        assert \"\" == result\n\n    async def test_read_json(self) -> None:\n        with Stream(b'{\"test\": \"passed\"}%s--:--' % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json(encoding=\"cp1251\")\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_guess_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json; charset=cp1251\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_compressed(self) -> None:\n        with Stream(\n            b\"\\xabV*I-.Q\\xb2RP*H,.NMQ\\xaa\\x05\\x00\" b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY, {CONTENT_TYPE: \"application/json\"}, stream\n            )\n            obj._at_eof = True\n            result = await obj.json()\n        assert result is None\n\n    async def test_read_form(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_invalid_utf8(self) -> None:\n        invalid_unicode_byte = b\"\\xff\"\n        data = invalid_unicode_byte + b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            with pytest.raises(\n                ValueError, match=\"data cannot be decoded with utf-8 encoding\"\n            ):\n                await obj.form()\n\n    async def test_read_form_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form(encoding=\"cp1251\")\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_guess_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded; charset=utf-8\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            obj._at_eof = True\n            result = await obj.form()\n        assert not result\n\n    async def test_readline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.readline()\n            assert b\"Hello\\n\" == result\n            result = await obj.readline()\n            assert b\",\\r\\n\" == result\n            result = await obj.readline()\n            assert b\"world!\" == result\n            result = await obj.readline()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n            await obj.release()\n            assert obj.at_eof()\n            assert remained == stream.content.read()\n\n    async def test_release_respects_content_length(self) -> None:\n        with Stream(b\".\" * 100500 + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.release()\n            assert result is None\n            assert obj.at_eof()\n\n    async def test_release_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            await obj.release()\n            await obj.release()\n            assert remained == stream.content.read()\n\n    async def test_filename(self) -> None:\n        part = aiohttp.BodyPartReader(\n            BOUNDARY, {CONTENT_DISPOSITION: \"attachment; filename=foo.html\"}, None\n        )\n        assert \"foo.html\" == part.filename\n\n    async def test_reading_long_part(self) -> None:\n        size = 2 * 2**16\n        protocol = mock.Mock(_reading_paused=False)\n        stream = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop())\n        stream.feed_data(b\"0\" * size + b\"\\r\\n--:--\")\n        stream.feed_eof()\n        obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n        data = await obj.read()\n        assert len(data) == size\n\n\nclass TestMultipartReader:\n    def test_from_response(self) -> None:\n        with Stream(b\"--:%s\\r\\nhello%s--:--\" % (newline, newline)) as stream:\n            resp = Response(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = aiohttp.MultipartReader.from_response(resp)\n        assert isinstance(res, MultipartResponseWrapper)\n        assert isinstance(res.stream, aiohttp.MultipartReader)\n\n    def test_bad_boundary(self) -> None:\n        with Stream(b\"\") as stream:\n            resp = Response(\n                {CONTENT_TYPE: \"multipart/related;boundary=\" + \"a\" * 80}, stream\n            )\n            with pytest.raises(ValueError):\n                aiohttp.MultipartReader.from_response(resp)\n\n    def test_dispatch(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_bodypart(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_multipart(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\" b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader(\n                {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n            )\n        assert isinstance(res, reader.__class__)\n\n    def test_dispatch_custom_multipart_reader(self) -> None:\n        class CustomReader(aiohttp.MultipartReader):\n            pass\n\n        reader = aiohttp.MultipartReader(\n            {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n            Stream(\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"test\\r\\n\"\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"passed\\r\\n\"\n                b\"----:----\\r\\n\"\n                b\"--:--\"\n            ),\n        )\n        reader.multipart_reader_cls = CustomReader\n        res = reader._get_part_reader(\n            {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n        )\n        assert isinstance(res, CustomReader)\n\n    async def test_emit_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = await reader.next()\n        assert isinstance(res, reader.part_reader_cls)\n\n    async def test_invalid_boundary(self) -> None:\n        with Stream(b\"---:%s\\r\\necho%s---:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(ValueError):\n                await reader.next()\n\n    async def test_release(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Type: multipart/related;boundary=--:--\",\n                    b\"\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\",\n                    b\"\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/mixed;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_release(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            res = await reader.next()\n            assert res is None\n\n    async def test_second_next_releases_previous_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_release_without_read_the_last_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            second = await reader.next()\n            third = await reader.next()\n\n            assert first.at_eof()\n            assert second.at_eof()\n            assert second.at_eof()\n            assert third is None\n\n    async def test_read_chunk_by_length_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Length: 4\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"Content-Length: 6\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    read_part += await part.read_chunk(3)\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"test\", b\"passed\"]\n\n    async def test_read_chunk_from_stream_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"chunk\",\n                    b\"--:\",\n                    b\"\",\n                    b\"two_chunks\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    chunk = await part.read_chunk(5)\n                    assert chunk\n                    read_part += chunk\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"chunk\", b\"two_chunks\"]\n\n    async def test_reading_skips_prelude(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"Multi-part data is not supported.\",\n                    b\"\",\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_read_form_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"ascii\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain;charset=UTF-8\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field2\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field3\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            field1 = await reader.next()\n            assert field1.name == \"field1\"\n            assert field1.get_charset(\"default\") == \"ascii\"\n            field2 = await reader.next()\n            assert field2.name == \"field2\"\n            assert field2.get_charset(\"default\") == \"UTF-8\"\n            field3 = await reader.next()\n            assert field3.name == \"field3\"\n            assert field3.get_charset(\"default\") == \"ascii\"\n\n    async def test_read_form_invalid_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"this-value-is-too-long-to-be-a-charset\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(RuntimeError, match=\"Invalid default charset\"):\n                await reader.next()\n\n\nasync def test_writer(writer) -> None:\n    assert writer.size == 7\n    assert writer.boundary == \":\"\n\n\nasync def test_writer_serialize_io_chunk(buf, stream, writer) -> None:\n    with io.BytesIO(b\"foobarbaz\") as file_handle:\n        writer.append(file_handle)\n        await writer.write(stream)\n    assert (\n        buf == b\"--:\\r\\nContent-Type: application/octet-stream\"\n        b\"\\r\\nContent-Length: 9\\r\\n\\r\\nfoobarbaz\\r\\n--:--\\r\\n\"\n    )\n\n\nasync def test_writer_serialize_json(buf, stream, writer) -> None:\n    writer.append_json({\"привет\": \"мир\"})\n    await writer.write(stream)\n    assert (\n        b'{\"\\\\u043f\\\\u0440\\\\u0438\\\\u0432\\\\u0435\\\\u0442\":'\n        b' \"\\\\u043c\\\\u0438\\\\u0440\"}' in buf\n    )\n\n\nasync def test_writer_serialize_form(buf, stream, writer) -> None:\n    data = [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"zoo\")]\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"foo=bar&foo=baz&boo=zoo\" in buf\n\n\nasync def test_writer_serialize_form_dict(buf, stream, writer) -> None:\n    data = {\"hello\": \"мир\"}\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"hello=%D0%BC%D0%B8%D1%80\" in buf\n\n\nasync def test_writer_write(buf, stream, writer) -> None:\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n\n    sub_multipart = aiohttp.MultipartWriter(boundary=\"::\")\n    sub_multipart.append(\"nested content\")\n    sub_multipart.headers[\"X-CUSTOM\"] = \"test\"\n    writer.append(sub_multipart)\n    await writer.write(stream)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b'Content-Type: multipart/mixed; boundary=\"::\"\\r\\n'\n        b\"X-CUSTOM: test\\r\\nContent-Length: 93\\r\\n\\r\\n\"\n        b\"--::\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 14\\r\\n\\r\\n\"\n        b\"nested content\\r\\n\"\n        b\"--::--\\r\\n\"\n        b\"\\r\\n\"\n        b\"--:--\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_close_boundary(buf, stream) -> None:\n    writer = aiohttp.MultipartWriter(boundary=\":\")\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n    await writer.write(stream, close_boundary=False)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_parts(buf, stream, writer) -> None:\n    await writer.write(stream)\n    assert b\"--:--\\r\\n\" == bytes(buf)\n\n\nasync def test_writer_serialize_with_content_encoding_gzip(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"gzip\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: gzip\" == headers\n    )\n\n    decompressor = zlib.decompressobj(wbits=16 + zlib.MAX_WBITS)\n    data = decompressor.decompress(message.split(b\"\\r\\n\")[0])\n    data += decompressor.flush()\n    assert b\"Time to Relax!\" == data\n\n\nasync def test_writer_serialize_with_content_encoding_deflate(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"deflate\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: deflate\" == headers\n    )\n\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\\r\\n--:--\\r\\n\"\n    assert thing == message\n\n\nasync def test_writer_serialize_with_content_encoding_identity(buf, stream, writer):\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n    writer.append(thing, {CONTENT_ENCODING: \"identity\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: application/octet-stream\\r\\n\"\n        b\"Content-Encoding: identity\\r\\n\"\n        b\"Content-Length: 16\" == headers\n    )\n\n    assert thing == message.split(b\"\\r\\n\")[0]\n\n\ndef test_writer_serialize_with_content_encoding_unknown(buf, stream, writer):\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"snappy\"})\n\n\nasync def test_writer_with_content_transfer_encoding_base64(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"base64\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: base64\" == headers\n    )\n\n    assert b\"VGltZSB0byBSZWxheCE=\" == message.split(b\"\\r\\n\")[0]\n\n\nasync def test_writer_content_transfer_encoding_quote_printable(buf, stream, writer):\n    writer.append(\"Привет, мир!\", {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: quoted-printable\" == headers\n    )\n\n    assert (\n        b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n        b\" =D0=BC=D0=B8=D1=80!\" == message.split(b\"\\r\\n\")[0]\n    )\n\n\ndef test_writer_content_transfer_encoding_unknown(buf, stream, writer) -> None:\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"unknown\"})\n\n\nclass TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n        payload = writer.append(\"some-data\")\n        payload.set_content_disposition(\"form-data\", name=\"method\")\n        assert CONTENT_DISPOSITION in payload.headers\n        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )\n\n\nasync def test_async_for_reader() -> None:\n    data = [{\"test\": \"passed\"}, 42, b\"plain text\", b\"aiohttp\\n\", b\"no epilogue\"]\n    with Stream(\n        b\"\\r\\n\".join(\n            [\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[0]).encode(),\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[1]).encode(),\n                b\"--:\",\n                b'Content-Type: multipart/related; boundary=\"::\"',\n                b\"\",\n                b\"--::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[2],\n                b\"--::\",\n                b'Content-Disposition: attachment; filename=\"aiohttp\"',\n                b\"Content-Type: text/plain\",\n                b\"Content-Length: 28\",\n                b\"Content-Encoding: gzip\",\n                b\"\",\n                b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03K\\xcc\\xcc\\xcf())\"\n                b\"\\xe0\\x02\\x00\\xd6\\x90\\xe2O\\x08\\x00\\x00\\x00\",\n                b\"--::\",\n                b'Content-Type: multipart/related; boundary=\":::\"',\n                b\"\",\n                b\"--:::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[4],\n                b\"--:::--\",\n                b\"--::--\",\n                b\"\",\n                b\"--:--\",\n                b\"\",\n            ]\n        )\n    ) as stream:\n        reader = aiohttp.MultipartReader(\n            headers={CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'},\n            content=stream,\n        )\n        idata = iter(data)\n\n        async def check(reader):\n            async for part in reader:\n                if isinstance(part, aiohttp.BodyPartReader):\n                    if part.headers[CONTENT_TYPE] == \"application/json\":\n                        assert next(idata) == (await part.json())\n                    else:\n                        assert next(idata) == await part.read(decode=True)\n                else:\n                    await check(part)\n\n        await check(reader)\n\n\nasync def test_async_for_bodypart() -> None:\n    with Stream(b\"foobarbaz\\r\\n--:--\") as stream:\n        part = aiohttp.BodyPartReader(boundary=b\"--:\", headers={}, content=stream)\n        async for data in part:\n            assert data == b\"foobarbaz\"\n",
            "header": {
                "file": {
                    "path": "tests/test_multipart.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "dbfaf74b",
                "new_commit_hash": "37ac5479",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_multipart.py"
            },
            "plus_file": {
                "path": "b/tests/test_multipart.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1282,
                            "length": 6
                        },
                        "new_range": {
                            "start": 1282,
                            "length": 13
                        },
                        "section": "class TestMultipartWriter:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        part = writer._parts[0][0]",
                                "type": "context"
                            },
                            {
                                "content": "        assert part.headers[CONTENT_TYPE] == \"test/passed\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    async def test_set_content_disposition_after_append(self):",
                                "type": "added"
                            },
                            {
                                "content": "        writer = aiohttp.MultipartWriter(\"form-data\")",
                                "type": "added"
                            },
                            {
                                "content": "        payload = writer.append(\"some-data\")",
                                "type": "added"
                            },
                            {
                                "content": "        payload.set_content_disposition(\"form-data\", name=\"method\")",
                                "type": "added"
                            },
                            {
                                "content": "        assert CONTENT_DISPOSITION in payload.headers",
                                "type": "added"
                            },
                            {
                                "content": "        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def test_with(self) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "        with aiohttp.MultipartWriter(boundary=\":\") as writer:",
                                "type": "context"
                            },
                            {
                                "content": "            writer.append(\"foo\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_multipart.py",
                            "type": "class",
                            "name": "TestMultipartWriter",
                            "content": "class TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n        payload = writer.append(\"some-data\")\n        payload.set_content_disposition(\"form-data\", name=\"method\")\n        assert CONTENT_DISPOSITION in payload.headers\n        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1207,
                            "end_lineno": 1401,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "tests/test_multipart.py",
                            "type": "method",
                            "name": "TestMultipartWriter.test_set_content_disposition_after_append",
                            "content": "class TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n        payload = writer.append(\"some-data\")\n        payload.set_content_disposition(\"form-data\", name=\"method\")\n        assert CONTENT_DISPOSITION in payload.headers\n        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1285,
                            "end_lineno": 1290,
                            "parent": {
                                "file_name": "tests/test_multipart.py",
                                "type": "class",
                                "name": "TestMultipartWriter",
                                "content": "class TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n        payload = writer.append(\"some-data\")\n        payload.set_content_disposition(\"form-data\", name=\"method\")\n        assert CONTENT_DISPOSITION in payload.headers\n        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 1207,
                                "end_lineno": 1401,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "7eecdff163ccf029fbb1ddc9de4169d4aaeb6597^",
    "new_commit_hash": "7eecdff163ccf029fbb1ddc9de4169d4aaeb6597",
    "commit_message": "[PR #8332/482e6cdf backport][3.9] Add set_content_disposition test (#8333)\n\n**This is a backport of PR #8332 as merged into master\r\n(482e6cdf6516607360666a48c5828d3dbe959fbd).**\r\n\r\nCo-authored-by: Oleg A <t0rr@mail.ru>",
    "commit_date": "2024-04-15T20:47:19+01:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "7eecdff163ccf029fbb1ddc9de4169d4aaeb6597",
    "test_file_codes": [
        "import asyncio\nimport io\nimport json\nimport pathlib\nimport zlib\nfrom unittest import mock\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import payload\nfrom aiohttp.hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom aiohttp.helpers import parse_mimetype\nfrom aiohttp.multipart import MultipartResponseWrapper\nfrom aiohttp.streams import StreamReader\nfrom aiohttp.test_utils import make_mocked_coro\n\nBOUNDARY = b\"--:\"\n\n\nnewline = b\"\\r\\n\"\n\n\n@pytest.fixture\ndef buf():\n    return bytearray()\n\n\n@pytest.fixture\ndef stream(buf):\n    writer = mock.Mock()\n\n    async def write(chunk):\n        buf.extend(chunk)\n\n    writer.write.side_effect = write\n    return writer\n\n\n@pytest.fixture\ndef writer():\n    return aiohttp.MultipartWriter(boundary=\":\")\n\n\nclass Response:\n    def __init__(self, headers, content):\n        self.headers = headers\n        self.content = content\n\n\nclass Stream:\n    def __init__(self, content):\n        self.content = io.BytesIO(content)\n\n    async def read(self, size=None):\n        return self.content.read(size)\n\n    def at_eof(self):\n        return self.content.tell() == len(self.content.getbuffer())\n\n    async def readline(self):\n        return self.content.readline()\n\n    def unread_data(self, data):\n        self.content = io.BytesIO(data + self.content.read())\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.content.close()\n\n\nclass StreamWithShortenRead(Stream):\n    def __init__(self, content):\n        self._first = True\n        super().__init__(content)\n\n    async def read(self, size=None):\n        if size is not None and self._first:\n            self._first = False\n            size = size // 2\n        return await super().read(size)\n\n\nclass TestMultipartResponseWrapper:\n    def test_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.at_eof()\n        assert wrapper.resp.content.at_eof.called\n\n    async def test_next(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = False\n        await wrapper.next()\n        assert wrapper.stream.next.called\n\n    async def test_release(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        await wrapper.release()\n        assert wrapper.resp.release.called\n\n    async def test_release_when_stream_at_eof(self) -> None:\n        wrapper = MultipartResponseWrapper(mock.Mock(), mock.Mock())\n        wrapper.resp.release = make_mocked_coro(None)\n        wrapper.stream.next = make_mocked_coro(b\"\")\n        wrapper.stream.at_eof.return_value = True\n        await wrapper.next()\n        assert wrapper.stream.next.called\n        assert wrapper.resp.release.called\n\n\nclass TestPartReader:\n    async def test_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_next_next(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.next()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n            result = await obj.next()\n            assert result is None\n\n    async def test_read(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_at_eof(self) -> None:\n        with Stream(b\"--:\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            obj._at_eof = True\n            result = await obj.read_chunk()\n        assert b\"\" == result\n\n    async def test_read_chunk_without_content_length(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            c1 = await obj.read_chunk(8)\n            c2 = await obj.read_chunk(8)\n            c3 = await obj.read_chunk(8)\n        assert c1 + c2 == b\"Hello, world!\"\n        assert c3 == b\"\"\n\n    async def test_read_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, \"),\n                    prepare(b\"World\"),\n                    prepare(b\"!%s--:\" % newline),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(8)\n                assert c1 == b\"Hello, \"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"World\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"!\"\n\n    async def test_read_all_at_once(self) -> None:\n        data = b\"Hello, World!%s--:--%s\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk()\n            assert b\"Hello, World!\" == result\n            result = await obj.read_chunk()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_incomplete_body_chunked(self) -> None:\n        data = b\"Hello, World!%s--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = b\"\"\n            with pytest.raises(AssertionError):\n                for _ in range(4):\n                    result += await obj.read_chunk(7)\n        assert data == result\n\n    async def test_read_boundary_with_incomplete_chunk(self) -> None:\n        with Stream(b\"\") as stream:\n\n            def prepare(data):\n                return data\n\n            with mock.patch.object(\n                stream,\n                \"read\",\n                side_effect=[\n                    prepare(b\"Hello, World\"),\n                    prepare(b\"!%s\" % newline),\n                    prepare(b\"--:\"),\n                    prepare(b\"\"),\n                ],\n            ):\n                obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n                c1 = await obj.read_chunk(12)\n                assert c1 == b\"Hello, World\"\n                c2 = await obj.read_chunk(8)\n                assert c2 == b\"!\"\n                c3 = await obj.read_chunk(8)\n                assert c3 == b\"\"\n\n    async def test_multi_read_chunk(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read_chunk(8)\n            assert b\"Hello,\" == result\n            result = await obj.read_chunk(8)\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_chunk_properly_counts_read_bytes(self) -> None:\n        expected = b\".\" * 10\n        tail = b\"%s--:--\" % newline\n        size = len(expected)\n        with StreamWithShortenRead(expected + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": size},\n                stream,\n            )\n            result = bytearray()\n            while True:\n                chunk = await obj.read_chunk()\n                if not chunk:\n                    break\n                result.extend(chunk)\n        assert size == len(result)\n        assert b\".\" * size == result\n        assert obj.at_eof()\n\n    async def test_read_does_not_read_boundary(self) -> None:\n        data = b\"Hello, world!%s--:\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello, world!\" == result\n            assert b\"--:\" == (await stream.read())\n\n    async def test_multiread(self) -> None:\n        data = b\"Hello,%s--:%s%sworld!%s--:--\" % ((newline,) * 4)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello,\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_multiline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n            result = await obj.read()\n            assert b\"Hello\\n,\\r\\nworld!\" == result\n            result = await obj.read()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_read_respects_content_length(self) -> None:\n        data = b\".\" * 100500\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.read()\n            assert data == result\n            assert obj.at_eof()\n\n    async def test_read_with_content_encoding_gzip(self) -> None:\n        with Stream(\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n            b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"gzip\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_deflate(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_encoding_identity(self) -> None:\n        thing = (\n            b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x0b\\xc9\\xccMU\"\n            b\"(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00$\\xfb\\x9eV\\x0e\\x00\\x00\\x00\"\n        )\n        with Stream(thing + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"identity\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert thing == result\n\n    async def test_read_with_content_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"snappy\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_with_content_transfer_encoding_base64(self) -> None:\n        with Stream(b\"VGltZSB0byBSZWxheCE=%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"base64\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert b\"Time to Relax!\" == result\n\n    async def test_read_with_content_transfer_encoding_quoted_printable(self) -> None:\n        with Stream(\n            b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n            b\" =D0=BC=D0=B8=D1=80!%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        expected = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        assert result == expected\n\n    @pytest.mark.parametrize(\"encoding\", (\"binary\", \"8bit\", \"7bit\"))\n    async def test_read_with_content_transfer_encoding_binary(\n        self, encoding: str\n    ) -> None:\n        data = (\n            b\"\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82,\"\n            b\" \\xd0\\xbc\\xd0\\xb8\\xd1\\x80!\"\n        )\n        with Stream(data + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: encoding},\n                stream,\n            )\n            result = await obj.read(decode=True)\n        assert data == result\n\n    async def test_read_with_content_transfer_encoding_unknown(self) -> None:\n        with Stream(b\"\\x0e4Time to Relax!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TRANSFER_ENCODING: \"unknown\"},\n                stream,\n            )\n            with pytest.raises(RuntimeError):\n                await obj.read(decode=True)\n\n    async def test_read_text(self) -> None:\n        with Stream(b\"Hello, world!%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Hello, world!\" == result\n\n    async def test_read_text_default_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"utf-8\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.text(encoding=\"cp1251\")\n        assert data == result\n\n    async def test_read_text_guess_encoding(self) -> None:\n        data = \"Привет, Мир!\"\n        tail = b\"%s--:--\" % newline\n        with Stream(data.encode(\"cp1251\") + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"text/plain;charset=cp1251\"},\n                stream,\n            )\n            result = await obj.text()\n        assert data == result\n\n    async def test_read_text_compressed(self) -> None:\n        data = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\" b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"text/plain\"},\n                stream,\n            )\n            result = await obj.text()\n        assert \"Time to Relax!\" == result\n\n    async def test_read_text_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(BOUNDARY, {CONTENT_TYPE: \"text/plain\"}, stream)\n            obj._at_eof = True\n            result = await obj.text()\n        assert \"\" == result\n\n    async def test_read_json(self) -> None:\n        with Stream(b'{\"test\": \"passed\"}%s--:--' % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json(encoding=\"cp1251\")\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_guess_encoding(self) -> None:\n        data = '{\"тест\": \"пассед\"}'.encode(\"cp1251\")\n        tail = b\"%s--:--\" % newline\n        with Stream(data + tail) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/json; charset=cp1251\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"тест\": \"пассед\"} == result\n\n    async def test_read_json_compressed(self) -> None:\n        with Stream(\n            b\"\\xabV*I-.Q\\xb2RP*H,.NMQ\\xaa\\x05\\x00\" b\"%s--:--\" % newline\n        ) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_ENCODING: \"deflate\", CONTENT_TYPE: \"application/json\"},\n                stream,\n            )\n            result = await obj.json()\n        assert {\"test\": \"passed\"} == result\n\n    async def test_read_json_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY, {CONTENT_TYPE: \"application/json\"}, stream\n            )\n            obj._at_eof = True\n            result = await obj.json()\n        assert result is None\n\n    async def test_read_form(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_invalid_utf8(self) -> None:\n        invalid_unicode_byte = b\"\\xff\"\n        data = invalid_unicode_byte + b\"%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            with pytest.raises(\n                ValueError, match=\"data cannot be decoded with utf-8 encoding\"\n            ):\n                await obj.form()\n\n    async def test_read_form_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            result = await obj.form(encoding=\"cp1251\")\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_guess_encoding(self) -> None:\n        data = b\"foo=bar&foo=baz&boo=%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded; charset=utf-8\"},\n                stream,\n            )\n            result = await obj.form()\n        assert [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"\")] == result\n\n    async def test_read_form_while_closed(self) -> None:\n        with Stream(b\"\") as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {CONTENT_TYPE: \"application/x-www-form-urlencoded\"},\n                stream,\n            )\n            obj._at_eof = True\n            result = await obj.form()\n        assert not result\n\n    async def test_readline(self) -> None:\n        data = b\"Hello\\n,\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            result = await obj.readline()\n            assert b\"Hello\\n\" == result\n            result = await obj.readline()\n            assert b\",\\r\\n\" == result\n            result = await obj.readline()\n            assert b\"world!\" == result\n            result = await obj.readline()\n            assert b\"\" == result\n            assert obj.at_eof()\n\n    async def test_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n            await obj.release()\n            assert obj.at_eof()\n            assert remained == stream.content.read()\n\n    async def test_release_respects_content_length(self) -> None:\n        with Stream(b\".\" * 100500 + b\"%s--:--\" % newline) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {\"CONTENT-LENGTH\": 100500},\n                stream,\n            )\n            result = await obj.release()\n            assert result is None\n            assert obj.at_eof()\n\n    async def test_release_release(self) -> None:\n        data = b\"Hello,%s--:\\r\\n\\r\\nworld!%s--:--\" % (newline, newline)\n        remained = b\"--:\\r\\n\\r\\nworld!%s--:--\" % newline\n        with Stream(data) as stream:\n            obj = aiohttp.BodyPartReader(\n                BOUNDARY,\n                {},\n                stream,\n            )\n            await obj.release()\n            await obj.release()\n            assert remained == stream.content.read()\n\n    async def test_filename(self) -> None:\n        part = aiohttp.BodyPartReader(\n            BOUNDARY, {CONTENT_DISPOSITION: \"attachment; filename=foo.html\"}, None\n        )\n        assert \"foo.html\" == part.filename\n\n    async def test_reading_long_part(self) -> None:\n        size = 2 * 2**16\n        protocol = mock.Mock(_reading_paused=False)\n        stream = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop())\n        stream.feed_data(b\"0\" * size + b\"\\r\\n--:--\")\n        stream.feed_eof()\n        obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)\n        data = await obj.read()\n        assert len(data) == size\n\n\nclass TestMultipartReader:\n    def test_from_response(self) -> None:\n        with Stream(b\"--:%s\\r\\nhello%s--:--\" % (newline, newline)) as stream:\n            resp = Response(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = aiohttp.MultipartReader.from_response(resp)\n        assert isinstance(res, MultipartResponseWrapper)\n        assert isinstance(res.stream, aiohttp.MultipartReader)\n\n    def test_bad_boundary(self) -> None:\n        with Stream(b\"\") as stream:\n            resp = Response(\n                {CONTENT_TYPE: \"multipart/related;boundary=\" + \"a\" * 80}, stream\n            )\n            with pytest.raises(ValueError):\n                aiohttp.MultipartReader.from_response(resp)\n\n    def test_dispatch(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_bodypart(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader({CONTENT_TYPE: \"text/plain\"})\n        assert isinstance(res, reader.part_reader_cls)\n\n    def test_dispatch_multipart(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\" b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = reader._get_part_reader(\n                {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n            )\n        assert isinstance(res, reader.__class__)\n\n    def test_dispatch_custom_multipart_reader(self) -> None:\n        class CustomReader(aiohttp.MultipartReader):\n            pass\n\n        reader = aiohttp.MultipartReader(\n            {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n            Stream(\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"test\\r\\n\"\n                b\"----:--\\r\\n\"\n                b\"\\r\\n\"\n                b\"passed\\r\\n\"\n                b\"----:----\\r\\n\"\n                b\"--:--\"\n            ),\n        )\n        reader.multipart_reader_cls = CustomReader\n        res = reader._get_part_reader(\n            {CONTENT_TYPE: \"multipart/related;boundary=--:--\"}\n        )\n        assert isinstance(res, CustomReader)\n\n    async def test_emit_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            res = await reader.next()\n        assert isinstance(res, reader.part_reader_cls)\n\n    async def test_invalid_boundary(self) -> None:\n        with Stream(b\"---:%s\\r\\necho%s---:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(ValueError):\n                await reader.next()\n\n    async def test_release(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Type: multipart/related;boundary=--:--\",\n                    b\"\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"test\",\n                    b\"----:--\",\n                    b\"\",\n                    b\"passed\",\n                    b\"----:----\",\n                    b\"\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/mixed;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_release(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            await reader.release()\n            assert reader.at_eof()\n\n    async def test_release_next(self) -> None:\n        with Stream(b\"--:%s\\r\\necho%s--:--\" % (newline, newline)) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            await reader.release()\n            assert reader.at_eof()\n            res = await reader.next()\n            assert res is None\n\n    async def test_second_next_releases_previous_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_release_without_read_the_last_object(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            second = await reader.next()\n            third = await reader.next()\n\n            assert first.at_eof()\n            assert second.at_eof()\n            assert second.at_eof()\n            assert third is None\n\n    async def test_read_chunk_by_length_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"Content-Length: 4\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"Content-Length: 6\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    read_part += await part.read_chunk(3)\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"test\", b\"passed\"]\n\n    async def test_read_chunk_from_stream_doesnt_breaks_reader(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"--:\",\n                    b\"\",\n                    b\"chunk\",\n                    b\"--:\",\n                    b\"\",\n                    b\"two_chunks\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            body_parts = []\n            while True:\n                read_part = b\"\"\n                part = await reader.next()\n                if part is None:\n                    break\n                while not part.at_eof():\n                    chunk = await part.read_chunk(5)\n                    assert chunk\n                    read_part += chunk\n                body_parts.append(read_part)\n\n        assert body_parts == [b\"chunk\", b\"two_chunks\"]\n\n    async def test_reading_skips_prelude(self) -> None:\n        with Stream(\n            newline.join(\n                [\n                    b\"Multi-part data is not supported.\",\n                    b\"\",\n                    b\"--:\",\n                    b\"\",\n                    b\"test\",\n                    b\"--:\",\n                    b\"\",\n                    b\"passed\",\n                    b\"--:--\",\n                ]\n            )\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/related;boundary=\":\"'},\n                stream,\n            )\n            first = await reader.next()\n            assert isinstance(first, aiohttp.BodyPartReader)\n            second = await reader.next()\n\n            assert first.at_eof()\n            assert not second.at_eof()\n\n    async def test_read_form_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"ascii\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain;charset=UTF-8\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field2\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field3\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            field1 = await reader.next()\n            assert field1.name == \"field1\"\n            assert field1.get_charset(\"default\") == \"ascii\"\n            field2 = await reader.next()\n            assert field2.name == \"field2\"\n            assert field2.get_charset(\"default\") == \"UTF-8\"\n            field3 = await reader.next()\n            assert field3.name == \"field3\"\n            assert field3.get_charset(\"default\") == \"ascii\"\n\n    async def test_read_form_invalid_default_encoding(self) -> None:\n        with Stream(\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"_charset_\"\\r\\n\\r\\n'\n            b\"this-value-is-too-long-to-be-a-charset\"\n            b\"\\r\\n\"\n            b\"--:\\r\\n\"\n            b'Content-Disposition: form-data; name=\"field1\"\\r\\n\\r\\n'\n            b\"foo\"\n            b\"\\r\\n\"\n        ) as stream:\n            reader = aiohttp.MultipartReader(\n                {CONTENT_TYPE: 'multipart/form-data;boundary=\":\"'},\n                stream,\n            )\n            with pytest.raises(RuntimeError, match=\"Invalid default charset\"):\n                await reader.next()\n\n\nasync def test_writer(writer) -> None:\n    assert writer.size == 7\n    assert writer.boundary == \":\"\n\n\nasync def test_writer_serialize_io_chunk(buf, stream, writer) -> None:\n    with io.BytesIO(b\"foobarbaz\") as file_handle:\n        writer.append(file_handle)\n        await writer.write(stream)\n    assert (\n        buf == b\"--:\\r\\nContent-Type: application/octet-stream\"\n        b\"\\r\\nContent-Length: 9\\r\\n\\r\\nfoobarbaz\\r\\n--:--\\r\\n\"\n    )\n\n\nasync def test_writer_serialize_json(buf, stream, writer) -> None:\n    writer.append_json({\"привет\": \"мир\"})\n    await writer.write(stream)\n    assert (\n        b'{\"\\\\u043f\\\\u0440\\\\u0438\\\\u0432\\\\u0435\\\\u0442\":'\n        b' \"\\\\u043c\\\\u0438\\\\u0440\"}' in buf\n    )\n\n\nasync def test_writer_serialize_form(buf, stream, writer) -> None:\n    data = [(\"foo\", \"bar\"), (\"foo\", \"baz\"), (\"boo\", \"zoo\")]\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"foo=bar&foo=baz&boo=zoo\" in buf\n\n\nasync def test_writer_serialize_form_dict(buf, stream, writer) -> None:\n    data = {\"hello\": \"мир\"}\n    writer.append_form(data)\n    await writer.write(stream)\n\n    assert b\"hello=%D0%BC%D0%B8%D1%80\" in buf\n\n\nasync def test_writer_write(buf, stream, writer) -> None:\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n\n    sub_multipart = aiohttp.MultipartWriter(boundary=\"::\")\n    sub_multipart.append(\"nested content\")\n    sub_multipart.headers[\"X-CUSTOM\"] = \"test\"\n    writer.append(sub_multipart)\n    await writer.write(stream)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b'Content-Type: multipart/mixed; boundary=\"::\"\\r\\n'\n        b\"X-CUSTOM: test\\r\\nContent-Length: 93\\r\\n\\r\\n\"\n        b\"--::\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 14\\r\\n\\r\\n\"\n        b\"nested content\\r\\n\"\n        b\"--::--\\r\\n\"\n        b\"\\r\\n\"\n        b\"--:--\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_close_boundary(buf, stream) -> None:\n    writer = aiohttp.MultipartWriter(boundary=\":\")\n    writer.append(\"foo-bar-baz\")\n    writer.append_json({\"test\": \"passed\"})\n    writer.append_form({\"test\": \"passed\"})\n    writer.append_form([(\"one\", 1), (\"two\", 2)])\n    await writer.write(stream, close_boundary=False)\n\n    assert (\n        b\"--:\\r\\n\"\n        b\"Content-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"foo-bar-baz\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/json\\r\\n\"\n        b\"Content-Length: 18\\r\\n\\r\\n\"\n        b'{\"test\": \"passed\"}'\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"test=passed\"\n        b\"\\r\\n\"\n        b\"--:\\r\\n\"\n        b\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n        b\"Content-Length: 11\\r\\n\\r\\n\"\n        b\"one=1&two=2\"\n        b\"\\r\\n\"\n    ) == bytes(buf)\n\n\nasync def test_writer_write_no_parts(buf, stream, writer) -> None:\n    await writer.write(stream)\n    assert b\"--:--\\r\\n\" == bytes(buf)\n\n\nasync def test_writer_serialize_with_content_encoding_gzip(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"gzip\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: gzip\" == headers\n    )\n\n    decompressor = zlib.decompressobj(wbits=16 + zlib.MAX_WBITS)\n    data = decompressor.decompress(message.split(b\"\\r\\n\")[0])\n    data += decompressor.flush()\n    assert b\"Time to Relax!\" == data\n\n\nasync def test_writer_serialize_with_content_encoding_deflate(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"deflate\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Encoding: deflate\" == headers\n    )\n\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\\r\\n--:--\\r\\n\"\n    assert thing == message\n\n\nasync def test_writer_serialize_with_content_encoding_identity(buf, stream, writer):\n    thing = b\"\\x0b\\xc9\\xccMU(\\xc9W\\x08J\\xcdI\\xacP\\x04\\x00\"\n    writer.append(thing, {CONTENT_ENCODING: \"identity\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: application/octet-stream\\r\\n\"\n        b\"Content-Encoding: identity\\r\\n\"\n        b\"Content-Length: 16\" == headers\n    )\n\n    assert thing == message.split(b\"\\r\\n\")[0]\n\n\ndef test_writer_serialize_with_content_encoding_unknown(buf, stream, writer):\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_ENCODING: \"snappy\"})\n\n\nasync def test_writer_with_content_transfer_encoding_base64(buf, stream, writer):\n    writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"base64\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: base64\" == headers\n    )\n\n    assert b\"VGltZSB0byBSZWxheCE=\" == message.split(b\"\\r\\n\")[0]\n\n\nasync def test_writer_content_transfer_encoding_quote_printable(buf, stream, writer):\n    writer.append(\"Привет, мир!\", {CONTENT_TRANSFER_ENCODING: \"quoted-printable\"})\n    await writer.write(stream)\n    headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n    assert (\n        b\"--:\\r\\nContent-Type: text/plain; charset=utf-8\\r\\n\"\n        b\"Content-Transfer-Encoding: quoted-printable\" == headers\n    )\n\n    assert (\n        b\"=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82,\"\n        b\" =D0=BC=D0=B8=D1=80!\" == message.split(b\"\\r\\n\")[0]\n    )\n\n\ndef test_writer_content_transfer_encoding_unknown(buf, stream, writer) -> None:\n    with pytest.raises(RuntimeError):\n        writer.append(\"Time to Relax!\", {CONTENT_TRANSFER_ENCODING: \"unknown\"})\n\n\nclass TestMultipartWriter:\n    def test_default_subtype(self, writer) -> None:\n        mimetype = parse_mimetype(writer.headers.get(CONTENT_TYPE))\n\n        assert \"multipart\" == mimetype.type\n        assert \"mixed\" == mimetype.subtype\n\n    def test_unquoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=\"abc123\")\n        expected = {CONTENT_TYPE: \"multipart/mixed; boundary=abc123\"}\n        assert expected == writer.headers\n\n    def test_quoted_boundary(self) -> None:\n        writer = aiohttp.MultipartWriter(boundary=R\"\\\"\")\n        expected = {CONTENT_TYPE: R'multipart/mixed; boundary=\"\\\\\\\"\"'}\n        assert expected == writer.headers\n\n    def test_bad_boundary(self) -> None:\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"тест\")\n        with pytest.raises(ValueError):\n            aiohttp.MultipartWriter(boundary=\"test\\n\")\n\n    def test_default_headers(self, writer) -> None:\n        expected = {CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'}\n        assert expected == writer.headers\n\n    def test_iter_parts(self, writer) -> None:\n        writer.append(\"foo\")\n        writer.append(\"bar\")\n        writer.append(\"baz\")\n        assert 3 == len(list(writer))\n\n    def test_append(self, writer) -> None:\n        assert 0 == len(writer)\n        writer.append(\"hello, world!\")\n        assert 1 == len(writer)\n        assert isinstance(writer._parts[0][0], payload.Payload)\n\n    def test_append_with_headers(self, writer) -> None:\n        writer.append(\"hello, world!\", {\"x-foo\": \"bar\"})\n        assert 1 == len(writer)\n        assert \"x-foo\" in writer._parts[0][0].headers\n        assert writer._parts[0][0].headers[\"x-foo\"] == \"bar\"\n\n    def test_append_json(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"application/json\"\n\n    def test_append_part(self, writer) -> None:\n        part = payload.get_payload(\"test\", headers={CONTENT_TYPE: \"text/plain\"})\n        writer.append(part, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_json_overrides_content_type(self, writer) -> None:\n        writer.append_json({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_form(self, writer) -> None:\n        writer.append_form({\"foo\": \"bar\"}, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    def test_append_multipart(self, writer) -> None:\n        subwriter = aiohttp.MultipartWriter(boundary=\":\")\n        subwriter.append_json({\"foo\": \"bar\"})\n        writer.append(subwriter, {CONTENT_TYPE: \"test/passed\"})\n        assert 1 == len(writer)\n        part = writer._parts[0][0]\n        assert part.headers[CONTENT_TYPE] == \"test/passed\"\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n        payload = writer.append(\"some-data\")\n        payload.set_content_disposition(\"form-data\", name=\"method\")\n        assert CONTENT_DISPOSITION in payload.headers\n        assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n\n    def test_with(self) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            writer.append(\"foo\")\n            writer.append(b\"bar\")\n            writer.append_json({\"baz\": True})\n        assert 3 == len(writer)\n\n    def test_append_int_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1)\n\n    def test_append_float_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(1.1)\n\n    def test_append_none_not_allowed(self) -> None:\n        with pytest.raises(TypeError):\n            with aiohttp.MultipartWriter(boundary=\":\") as writer:\n                writer.append(None)\n\n    async def test_write_preserves_content_disposition(self, buf, stream) -> None:\n        with aiohttp.MultipartWriter(boundary=\":\") as writer:\n            part = writer.append(b\"foo\", headers={CONTENT_TYPE: \"test/passed\"})\n            part.set_content_disposition(\"form-data\", filename=\"bug\")\n        await writer.write(stream)\n\n        headers, message = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: test/passed\\r\\n\"\n            b\"Content-Length: 3\\r\\n\"\n            b\"Content-Disposition:\"\n            b' form-data; filename=\"bug\"'\n        )\n        assert message == b\"foo\\r\\n--:--\\r\\n\"\n\n    async def test_preserve_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_set_content_disposition_override(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={\n                        CONTENT_DISPOSITION: 'attachments; filename=\"bug.py\"',\n                        CONTENT_TYPE: \"text/python\",\n                    },\n                )\n            await writer.write(stream)\n\n        assert part.headers[CONTENT_TYPE] == \"text/python\"\n        assert part.headers[CONTENT_DISPOSITION] == ('attachments; filename=\"bug.py\"')\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/python\\r\\n\"\n            b'Content-Disposition: attachments; filename=\"bug.py\"'\n        )\n\n    async def test_reset_content_disposition_header(self, buf, stream):\n        # https://github.com/aio-libs/aiohttp/pull/3475#issuecomment-451072381\n        with pathlib.Path(__file__).open(\"rb\") as fobj:\n            with aiohttp.MultipartWriter(\"form-data\", boundary=\":\") as writer:\n                part = writer.append(\n                    fobj,\n                    headers={CONTENT_TYPE: \"text/plain\"},\n                )\n\n            assert CONTENT_DISPOSITION in part.headers\n\n            part.set_content_disposition(\"attachments\", filename=\"bug.py\")\n\n            await writer.write(stream)\n\n        headers, _ = bytes(buf).split(b\"\\r\\n\\r\\n\", 1)\n\n        assert headers == (\n            b\"--:\\r\\n\"\n            b\"Content-Type: text/plain\\r\\n\"\n            b\"Content-Disposition:\"\n            b' attachments; filename=\"bug.py\"'\n        )\n\n\nasync def test_async_for_reader() -> None:\n    data = [{\"test\": \"passed\"}, 42, b\"plain text\", b\"aiohttp\\n\", b\"no epilogue\"]\n    with Stream(\n        b\"\\r\\n\".join(\n            [\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[0]).encode(),\n                b\"--:\",\n                b\"Content-Type: application/json\",\n                b\"\",\n                json.dumps(data[1]).encode(),\n                b\"--:\",\n                b'Content-Type: multipart/related; boundary=\"::\"',\n                b\"\",\n                b\"--::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[2],\n                b\"--::\",\n                b'Content-Disposition: attachment; filename=\"aiohttp\"',\n                b\"Content-Type: text/plain\",\n                b\"Content-Length: 28\",\n                b\"Content-Encoding: gzip\",\n                b\"\",\n                b\"\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x03K\\xcc\\xcc\\xcf())\"\n                b\"\\xe0\\x02\\x00\\xd6\\x90\\xe2O\\x08\\x00\\x00\\x00\",\n                b\"--::\",\n                b'Content-Type: multipart/related; boundary=\":::\"',\n                b\"\",\n                b\"--:::\",\n                b\"Content-Type: text/plain\",\n                b\"\",\n                data[4],\n                b\"--:::--\",\n                b\"--::--\",\n                b\"\",\n                b\"--:--\",\n                b\"\",\n            ]\n        )\n    ) as stream:\n        reader = aiohttp.MultipartReader(\n            headers={CONTENT_TYPE: 'multipart/mixed; boundary=\":\"'},\n            content=stream,\n        )\n        idata = iter(data)\n\n        async def check(reader):\n            async for part in reader:\n                if isinstance(part, aiohttp.BodyPartReader):\n                    if part.headers[CONTENT_TYPE] == \"application/json\":\n                        assert next(idata) == (await part.json())\n                    else:\n                        assert next(idata) == await part.read(decode=True)\n                else:\n                    await check(part)\n\n        await check(reader)\n\n\nasync def test_async_for_bodypart() -> None:\n    with Stream(b\"foobarbaz\\r\\n--:--\") as stream:\n        part = aiohttp.BodyPartReader(boundary=b\"--:\", headers={}, content=stream)\n        async for data in part:\n            assert data == b\"foobarbaz\"\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_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/requirements/cython.txt\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/hdrs.py\n./tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_helpers.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_http_parser.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_http_writer.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_websocket.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_helpers.pyi\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_cparser.pxd\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/_find_header.pxd\ncython -3 -o aiohttp/_helpers.c aiohttp/_helpers.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_parser.c aiohttp/_http_parser.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_writer.c aiohttp/_http_writer.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_websocket.c aiohttp/_websocket.pyx -I aiohttp -Werror\ncd vendor/llhttp; npm ci\n\n> llhttp@9.2.1 prepare\n> npm run clean && npm run build-ts\n\n\n> llhttp@9.2.1 clean\n> rm -rf lib && rm -rf test/tmp\n\n\n> llhttp@9.2.1 build-ts\n> tsc\n\n\nadded 213 packages, and audited 214 packages in 5s\n\n41 packages are looking for funding\n  run `npm fund` for details\n\n5 vulnerabilities (3 moderate, 2 high)\n\nTo address issues that do not require attention, run:\n  npm audit fix\n\nSome issues need review, and may require choosing\na different dependency.\n\nRun `npm audit` for details.\nmake -C vendor/llhttp generate\nmake[1]: Entering directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/vendor/llhttp'\nnpx ts-node bin/generate.ts\nmake[1]: Leaving directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/vendor/llhttp'\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/__init__.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/abc.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/base_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/client.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/client_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/client_proto.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/client_reqrep.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/client_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/compression_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/connector.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/cookiejar.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/formdata.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/http.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/http_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/http_parser.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/http_websocket.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/http_writer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/locks.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/multipart.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/payload.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/payload_streamer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/pytest_plugin.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/resolver.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/streams.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/tcp_helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/test_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/tracing.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/typedefs.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_app.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_fileresponse.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_middlewares.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_request.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_response.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_routedef.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_runner.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_server.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_urldispatcher.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/web_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/worker.py\nuv pip install -e . -c requirements/runtime-deps.txt\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 2ms\nPrepared 1 package in 0.50ms\nInstalled 1 package in 26ms\n + pip==24.3.1\nResolved 3 packages in 9ms\nInstalled 3 packages in 19ms\n + cython==3.0.5\n + multidict==6.0.4\n + typing-extensions==4.7.1\nResolved 95 packages in 64ms\nUninstalled 1 package in 29ms\nInstalled 93 packages in 173ms\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.63s\nPrepared 1 package in 16.24s\nInstalled 1 package in 0.83ms\n + aiohttp==3.9.4.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597)\nResolved 24 packages in 13ms\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_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597\nconfigfile: setup.cfg\nplugins: mock-3.12.0, asyncio-0.23.8, cov-4.1.0\nasyncio: mode=strict\ncollecting ... collected 108 items\n\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_at_eof PASSED    [  0%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_next PASSED      [  1%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_release PASSED   [  2%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_release_when_stream_at_eof PASSED [  3%]\nr2e_tests/test_1.py::TestPartReader::test_next PASSED                    [  4%]\nr2e_tests/test_1.py::TestPartReader::test_next_next PASSED               [  5%]\nr2e_tests/test_1.py::TestPartReader::test_read PASSED                    [  6%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_at_eof PASSED       [  7%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_without_content_length PASSED [  8%]\nr2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk PASSED   [  9%]\nr2e_tests/test_1.py::TestPartReader::test_read_all_at_once PASSED        [ 10%]\nr2e_tests/test_1.py::TestPartReader::test_read_incomplete_body_chunked PASSED [ 11%]\nr2e_tests/test_1.py::TestPartReader::test_read_boundary_with_incomplete_chunk PASSED [ 12%]\nr2e_tests/test_1.py::TestPartReader::test_multi_read_chunk PASSED        [ 12%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_properly_counts_read_bytes PASSED [ 13%]\nr2e_tests/test_1.py::TestPartReader::test_read_does_not_read_boundary PASSED [ 14%]\nr2e_tests/test_1.py::TestPartReader::test_multiread PASSED               [ 15%]\nr2e_tests/test_1.py::TestPartReader::test_read_multiline PASSED          [ 16%]\nr2e_tests/test_1.py::TestPartReader::test_read_respects_content_length PASSED [ 17%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_gzip PASSED [ 18%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_deflate PASSED [ 19%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_identity PASSED [ 20%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_unknown PASSED [ 21%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_base64 PASSED [ 22%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_quoted_printable PASSED [ 23%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[binary] PASSED [ 24%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[8bit] PASSED [ 25%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[7bit] PASSED [ 25%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_unknown PASSED [ 26%]\nr2e_tests/test_1.py::TestPartReader::test_read_text PASSED               [ 27%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_default_encoding PASSED [ 28%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_encoding PASSED      [ 29%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_guess_encoding PASSED [ 30%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_compressed PASSED    [ 31%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_while_closed PASSED  [ 32%]\nr2e_tests/test_1.py::TestPartReader::test_read_json PASSED               [ 33%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_encoding PASSED      [ 34%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_guess_encoding PASSED [ 35%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_compressed PASSED    [ 36%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_while_closed PASSED  [ 37%]\nr2e_tests/test_1.py::TestPartReader::test_read_form PASSED               [ 37%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_invalid_utf8 PASSED  [ 38%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_encoding PASSED      [ 39%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_guess_encoding PASSED [ 40%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_while_closed PASSED  [ 41%]\nr2e_tests/test_1.py::TestPartReader::test_readline PASSED                [ 42%]\nr2e_tests/test_1.py::TestPartReader::test_release PASSED                 [ 43%]\nr2e_tests/test_1.py::TestPartReader::test_release_respects_content_length PASSED [ 44%]\nr2e_tests/test_1.py::TestPartReader::test_release_release PASSED         [ 45%]\nr2e_tests/test_1.py::TestPartReader::test_filename PASSED                [ 46%]\nr2e_tests/test_1.py::TestPartReader::test_reading_long_part PASSED       [ 47%]\nr2e_tests/test_1.py::TestMultipartReader::test_from_response PASSED      [ 48%]\nr2e_tests/test_1.py::TestMultipartReader::test_bad_boundary PASSED       [ 49%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch PASSED           [ 50%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_bodypart PASSED  [ 50%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_multipart PASSED [ 51%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_custom_multipart_reader PASSED [ 52%]\nr2e_tests/test_1.py::TestMultipartReader::test_emit_next PASSED          [ 53%]\nr2e_tests/test_1.py::TestMultipartReader::test_invalid_boundary PASSED   [ 54%]\nr2e_tests/test_1.py::TestMultipartReader::test_release PASSED            [ 55%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_release PASSED    [ 56%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_next PASSED       [ 57%]\nr2e_tests/test_1.py::TestMultipartReader::test_second_next_releases_previous_object PASSED [ 58%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_without_read_the_last_object PASSED [ 59%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_chunk_by_length_doesnt_breaks_reader PASSED [ 60%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_chunk_from_stream_doesnt_breaks_reader PASSED [ 61%]\nr2e_tests/test_1.py::TestMultipartReader::test_reading_skips_prelude PASSED [ 62%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_form_default_encoding PASSED [ 62%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_form_invalid_default_encoding PASSED [ 63%]\nr2e_tests/test_1.py::test_writer PASSED                                  [ 64%]\nr2e_tests/test_1.py::test_writer_serialize_io_chunk PASSED               [ 65%]\nr2e_tests/test_1.py::test_writer_serialize_json PASSED                   [ 66%]\nr2e_tests/test_1.py::test_writer_serialize_form PASSED                   [ 67%]\nr2e_tests/test_1.py::test_writer_serialize_form_dict PASSED              [ 68%]\nr2e_tests/test_1.py::test_writer_write PASSED                            [ 69%]\nr2e_tests/test_1.py::test_writer_write_no_close_boundary PASSED          [ 70%]\nr2e_tests/test_1.py::test_writer_write_no_parts PASSED                   [ 71%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_gzip PASSED [ 72%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_deflate PASSED [ 73%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_identity PASSED [ 74%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_unknown PASSED [ 75%]\nr2e_tests/test_1.py::test_writer_with_content_transfer_encoding_base64 PASSED [ 75%]\nr2e_tests/test_1.py::test_writer_content_transfer_encoding_quote_printable PASSED [ 76%]\nr2e_tests/test_1.py::test_writer_content_transfer_encoding_unknown PASSED [ 77%]\nr2e_tests/test_1.py::TestMultipartWriter::test_default_subtype PASSED    [ 78%]\nr2e_tests/test_1.py::TestMultipartWriter::test_unquoted_boundary PASSED  [ 79%]\nr2e_tests/test_1.py::TestMultipartWriter::test_quoted_boundary PASSED    [ 80%]\nr2e_tests/test_1.py::TestMultipartWriter::test_bad_boundary PASSED       [ 81%]\nr2e_tests/test_1.py::TestMultipartWriter::test_default_headers PASSED    [ 82%]\nr2e_tests/test_1.py::TestMultipartWriter::test_iter_parts PASSED         [ 83%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append PASSED             [ 84%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_with_headers PASSED [ 85%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_json PASSED        [ 86%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_part PASSED        [ 87%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_json_overrides_content_type PASSED [ 87%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_form PASSED        [ 88%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_multipart PASSED   [ 89%]\nr2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_after_append PASSED [ 90%]\nr2e_tests/test_1.py::TestMultipartWriter::test_with PASSED               [ 91%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_int_not_allowed PASSED [ 92%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_float_not_allowed PASSED [ 93%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_none_not_allowed PASSED [ 94%]\nr2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition PASSED [ 95%]\nr2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header PASSED [ 96%]\nr2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_override PASSED [ 97%]\nr2e_tests/test_1.py::TestMultipartWriter::test_reset_content_disposition_header PASSED [ 98%]\nr2e_tests/test_1.py::test_async_for_reader PASSED                        [ 99%]\nr2e_tests/test_1.py::test_async_for_bodypart 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      8      2      1    69%\naiohttp/abc.py                              90      8      0      0    91%\naiohttp/base_protocol.py                    67     51     22      0    18%\naiohttp/client.py                          493    372    182      2    19%\naiohttp/client_exceptions.py               135     61     16      0    49%\naiohttp/client_proto.py                    159    129     56      0    14%\naiohttp/client_reqrep.py                   681    508    292      0    18%\naiohttp/client_ws.py                       210    168     66      1    16%\naiohttp/compression_utils.py                70     18     14      2    67%\naiohttp/connector.py                       718    593    309      0    12%\naiohttp/cookiejar.py                       259    205    125      0    14%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         527    299    180     11    37%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     510    379    208      8    20%\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                       584     78    244     27    83%\naiohttp/payload.py                         219     52     56      8    72%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   167     97     42      5    40%\naiohttp/resolver.py                         68     48     22      0    22%\naiohttp/streams.py                         396    222    144     24    38%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      316    196     52      4    34%\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                145    121     48      0    12%\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    285    128      1    28%\naiohttp/web_response.py                    483    370    218      0    16%\naiohttp/web_routedef.py                    106     46     10      0    55%\naiohttp/web_runner.py                      216    133     38      0    33%\naiohttp/web_server.py                       41     22      8      0    39%\naiohttp/web_urldispatcher.py               719    459    161      0    30%\naiohttp/web_ws.py                          335    267    116      1    16%\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           2310   2310     76      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               784    784     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               483    483     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                   1529   1529     76      0     0%\ntests/test_cookiejar.py                    347    347     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                      469    469     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1053   1053     68      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    768    768     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                     165    165     12      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      520    520     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                  839    839     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              1536   1536     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      653    653     18      0     0%\ntests/test_web_server.py                   163    163      0      0     0%\ntests/test_web_urldispatcher.py            367    367     16      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     626    626     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                                    32708  28857   4170     96    11%\n\n============================= slowest 10 durations =============================\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header\n0.02s call     r2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_after_append\n0.02s call     r2e_tests/test_1.py::TestMultipartReader::test_emit_next\n0.02s call     r2e_tests/test_1.py::TestMultipartResponseWrapper::test_next\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_override\n0.02s call     r2e_tests/test_1.py::test_writer_serialize_io_chunk\n0.02s call     r2e_tests/test_1.py::TestMultipartReader::test_read_form_default_encoding\n0.02s call     r2e_tests/test_1.py::TestPartReader::test_read_json\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_at_eof\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_next\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_release\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_release_when_stream_at_eof\nPASSED r2e_tests/test_1.py::TestPartReader::test_next\nPASSED r2e_tests/test_1.py::TestPartReader::test_next_next\nPASSED r2e_tests/test_1.py::TestPartReader::test_read\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_at_eof\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_without_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_all_at_once\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_incomplete_body_chunked\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_boundary_with_incomplete_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_multi_read_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_properly_counts_read_bytes\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_does_not_read_boundary\nPASSED r2e_tests/test_1.py::TestPartReader::test_multiread\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_multiline\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_respects_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_gzip\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_deflate\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_identity\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_unknown\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_base64\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_quoted_printable\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[binary]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[8bit]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[7bit]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_unknown\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_default_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_compressed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_compressed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_invalid_utf8\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_readline\nPASSED r2e_tests/test_1.py::TestPartReader::test_release\nPASSED r2e_tests/test_1.py::TestPartReader::test_release_respects_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_release_release\nPASSED r2e_tests/test_1.py::TestPartReader::test_filename\nPASSED r2e_tests/test_1.py::TestPartReader::test_reading_long_part\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_from_response\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_bad_boundary\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_bodypart\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_multipart\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_custom_multipart_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_emit_next\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_invalid_boundary\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_release\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_next\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_second_next_releases_previous_object\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_without_read_the_last_object\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_chunk_by_length_doesnt_breaks_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_chunk_from_stream_doesnt_breaks_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_reading_skips_prelude\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_form_default_encoding\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_form_invalid_default_encoding\nPASSED r2e_tests/test_1.py::test_writer\nPASSED r2e_tests/test_1.py::test_writer_serialize_io_chunk\nPASSED r2e_tests/test_1.py::test_writer_serialize_json\nPASSED r2e_tests/test_1.py::test_writer_serialize_form\nPASSED r2e_tests/test_1.py::test_writer_serialize_form_dict\nPASSED r2e_tests/test_1.py::test_writer_write\nPASSED r2e_tests/test_1.py::test_writer_write_no_close_boundary\nPASSED r2e_tests/test_1.py::test_writer_write_no_parts\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_gzip\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_deflate\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_identity\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_unknown\nPASSED r2e_tests/test_1.py::test_writer_with_content_transfer_encoding_base64\nPASSED r2e_tests/test_1.py::test_writer_content_transfer_encoding_quote_printable\nPASSED r2e_tests/test_1.py::test_writer_content_transfer_encoding_unknown\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_default_subtype\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_unquoted_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_quoted_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_bad_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_default_headers\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_iter_parts\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_with_headers\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_json\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_part\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_json_overrides_content_type\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_form\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_multipart\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_after_append\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_with\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_int_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_float_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_none_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_override\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_reset_content_disposition_header\nPASSED r2e_tests/test_1.py::test_async_for_reader\nPASSED r2e_tests/test_1.py::test_async_for_bodypart\n============================= 108 passed in 4.81s ==============================\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_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597\nconfigfile: setup.cfg\nplugins: mock-3.12.0, asyncio-0.23.8, cov-4.1.0\nasyncio: mode=strict\ncollecting ... collected 108 items\n\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_at_eof PASSED    [  0%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_next PASSED      [  1%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_release PASSED   [  2%]\nr2e_tests/test_1.py::TestMultipartResponseWrapper::test_release_when_stream_at_eof PASSED [  3%]\nr2e_tests/test_1.py::TestPartReader::test_next PASSED                    [  4%]\nr2e_tests/test_1.py::TestPartReader::test_next_next PASSED               [  5%]\nr2e_tests/test_1.py::TestPartReader::test_read PASSED                    [  6%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_at_eof PASSED       [  7%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_without_content_length PASSED [  8%]\nr2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk PASSED   [  9%]\nr2e_tests/test_1.py::TestPartReader::test_read_all_at_once PASSED        [ 10%]\nr2e_tests/test_1.py::TestPartReader::test_read_incomplete_body_chunked PASSED [ 11%]\nr2e_tests/test_1.py::TestPartReader::test_read_boundary_with_incomplete_chunk PASSED [ 12%]\nr2e_tests/test_1.py::TestPartReader::test_multi_read_chunk PASSED        [ 12%]\nr2e_tests/test_1.py::TestPartReader::test_read_chunk_properly_counts_read_bytes PASSED [ 13%]\nr2e_tests/test_1.py::TestPartReader::test_read_does_not_read_boundary PASSED [ 14%]\nr2e_tests/test_1.py::TestPartReader::test_multiread PASSED               [ 15%]\nr2e_tests/test_1.py::TestPartReader::test_read_multiline PASSED          [ 16%]\nr2e_tests/test_1.py::TestPartReader::test_read_respects_content_length PASSED [ 17%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_gzip PASSED [ 18%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_deflate PASSED [ 19%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_identity PASSED [ 20%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_unknown PASSED [ 21%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_base64 PASSED [ 22%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_quoted_printable PASSED [ 23%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[binary] PASSED [ 24%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[8bit] PASSED [ 25%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[7bit] PASSED [ 25%]\nr2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_unknown PASSED [ 26%]\nr2e_tests/test_1.py::TestPartReader::test_read_text PASSED               [ 27%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_default_encoding PASSED [ 28%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_encoding PASSED      [ 29%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_guess_encoding PASSED [ 30%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_compressed PASSED    [ 31%]\nr2e_tests/test_1.py::TestPartReader::test_read_text_while_closed PASSED  [ 32%]\nr2e_tests/test_1.py::TestPartReader::test_read_json PASSED               [ 33%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_encoding PASSED      [ 34%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_guess_encoding PASSED [ 35%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_compressed PASSED    [ 36%]\nr2e_tests/test_1.py::TestPartReader::test_read_json_while_closed PASSED  [ 37%]\nr2e_tests/test_1.py::TestPartReader::test_read_form PASSED               [ 37%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_invalid_utf8 PASSED  [ 38%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_encoding PASSED      [ 39%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_guess_encoding PASSED [ 40%]\nr2e_tests/test_1.py::TestPartReader::test_read_form_while_closed PASSED  [ 41%]\nr2e_tests/test_1.py::TestPartReader::test_readline PASSED                [ 42%]\nr2e_tests/test_1.py::TestPartReader::test_release PASSED                 [ 43%]\nr2e_tests/test_1.py::TestPartReader::test_release_respects_content_length PASSED [ 44%]\nr2e_tests/test_1.py::TestPartReader::test_release_release PASSED         [ 45%]\nr2e_tests/test_1.py::TestPartReader::test_filename PASSED                [ 46%]\nr2e_tests/test_1.py::TestPartReader::test_reading_long_part PASSED       [ 47%]\nr2e_tests/test_1.py::TestMultipartReader::test_from_response PASSED      [ 48%]\nr2e_tests/test_1.py::TestMultipartReader::test_bad_boundary PASSED       [ 49%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch PASSED           [ 50%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_bodypart PASSED  [ 50%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_multipart PASSED [ 51%]\nr2e_tests/test_1.py::TestMultipartReader::test_dispatch_custom_multipart_reader PASSED [ 52%]\nr2e_tests/test_1.py::TestMultipartReader::test_emit_next PASSED          [ 53%]\nr2e_tests/test_1.py::TestMultipartReader::test_invalid_boundary PASSED   [ 54%]\nr2e_tests/test_1.py::TestMultipartReader::test_release PASSED            [ 55%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_release PASSED    [ 56%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_next PASSED       [ 57%]\nr2e_tests/test_1.py::TestMultipartReader::test_second_next_releases_previous_object PASSED [ 58%]\nr2e_tests/test_1.py::TestMultipartReader::test_release_without_read_the_last_object PASSED [ 59%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_chunk_by_length_doesnt_breaks_reader PASSED [ 60%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_chunk_from_stream_doesnt_breaks_reader PASSED [ 61%]\nr2e_tests/test_1.py::TestMultipartReader::test_reading_skips_prelude PASSED [ 62%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_form_default_encoding PASSED [ 62%]\nr2e_tests/test_1.py::TestMultipartReader::test_read_form_invalid_default_encoding PASSED [ 63%]\nr2e_tests/test_1.py::test_writer PASSED                                  [ 64%]\nr2e_tests/test_1.py::test_writer_serialize_io_chunk PASSED               [ 65%]\nr2e_tests/test_1.py::test_writer_serialize_json PASSED                   [ 66%]\nr2e_tests/test_1.py::test_writer_serialize_form PASSED                   [ 67%]\nr2e_tests/test_1.py::test_writer_serialize_form_dict PASSED              [ 68%]\nr2e_tests/test_1.py::test_writer_write PASSED                            [ 69%]\nr2e_tests/test_1.py::test_writer_write_no_close_boundary PASSED          [ 70%]\nr2e_tests/test_1.py::test_writer_write_no_parts PASSED                   [ 71%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_gzip PASSED [ 72%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_deflate PASSED [ 73%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_identity PASSED [ 74%]\nr2e_tests/test_1.py::test_writer_serialize_with_content_encoding_unknown PASSED [ 75%]\nr2e_tests/test_1.py::test_writer_with_content_transfer_encoding_base64 PASSED [ 75%]\nr2e_tests/test_1.py::test_writer_content_transfer_encoding_quote_printable PASSED [ 76%]\nr2e_tests/test_1.py::test_writer_content_transfer_encoding_unknown PASSED [ 77%]\nr2e_tests/test_1.py::TestMultipartWriter::test_default_subtype PASSED    [ 78%]\nr2e_tests/test_1.py::TestMultipartWriter::test_unquoted_boundary PASSED  [ 79%]\nr2e_tests/test_1.py::TestMultipartWriter::test_quoted_boundary PASSED    [ 80%]\nr2e_tests/test_1.py::TestMultipartWriter::test_bad_boundary PASSED       [ 81%]\nr2e_tests/test_1.py::TestMultipartWriter::test_default_headers PASSED    [ 82%]\nr2e_tests/test_1.py::TestMultipartWriter::test_iter_parts PASSED         [ 83%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append PASSED             [ 84%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_with_headers PASSED [ 85%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_json PASSED        [ 86%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_part PASSED        [ 87%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_json_overrides_content_type PASSED [ 87%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_form PASSED        [ 88%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_multipart PASSED   [ 89%]\nr2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_after_append FAILED [ 90%]\nr2e_tests/test_1.py::TestMultipartWriter::test_with PASSED               [ 91%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_int_not_allowed PASSED [ 92%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_float_not_allowed PASSED [ 93%]\nr2e_tests/test_1.py::TestMultipartWriter::test_append_none_not_allowed PASSED [ 94%]\nr2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition FAILED [ 95%]\nr2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header PASSED [ 96%]\nr2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_override PASSED [ 97%]\nr2e_tests/test_1.py::TestMultipartWriter::test_reset_content_disposition_header PASSED [ 98%]\nr2e_tests/test_1.py::test_async_for_reader PASSED                        [ 99%]\nr2e_tests/test_1.py::test_async_for_bodypart PASSED                      [100%]\n\n=================================== FAILURES ===================================\n________ TestMultipartWriter.test_set_content_disposition_after_append _________\n\nself = <r2e_tests.test_1.TestMultipartWriter object at 0x77a6f86d1dc0>\n\n    async def test_set_content_disposition_after_append(self):\n        writer = aiohttp.MultipartWriter(\"form-data\")\n>       payload = writer.append(\"some-data\")\n\nself       = <r2e_tests.test_1.TestMultipartWriter object at 0x77a6f86d1dc0>\nwriter     = <aiohttp.multipart.MultipartWriter object at 0x77a6f7ddd7f0>\n\nr2e_tests/test_1.py:1287: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \naiohttp/multipart.py:842: in append\n    return self.append_payload(payload)\n        headers    = <CIMultiDict()>\n        obj        = 'some-data'\n        payload    = <aiohttp.payload.StringPayload object at 0x77a6f7ddd8e0>\n        self       = <aiohttp.multipart.MultipartWriter object at 0x77a6f7ddd7f0>\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <aiohttp.multipart.MultipartWriter object at 0x77a6f7ddd7f0>\npayload = <aiohttp.payload.StringPayload object at 0x77a6f7ddd8e0>\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n>           assert CONTENT_DISPOSITION in payload.headers\nE           AssertionError\n\nencoding   = None\npayload    = <aiohttp.payload.StringPayload object at 0x77a6f7ddd8e0>\nself       = <aiohttp.multipart.MultipartWriter object at 0x77a6f7ddd7f0>\nte_encoding = None\n\naiohttp/multipart.py:851: AssertionError\n_________ TestMultipartWriter.test_write_preserves_content_disposition _________\n\ncls = <class '_pytest.runner.CallInfo'>\nfunc = <function call_runtest_hook.<locals>.<lambda> at 0x77a6f864bca0>\nwhen = 'call'\nreraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)\n\n    @classmethod\n    def from_call(\n        cls,\n        func: \"Callable[[], TResult]\",\n        when: \"Literal['collect', 'setup', 'call', 'teardown']\",\n        reraise: Optional[\n            Union[Type[BaseException], Tuple[Type[BaseException], ...]]\n        ] = None,\n    ) -> \"CallInfo[TResult]\":\n        \"\"\"Call func, wrapping the result in a CallInfo.\n    \n        :param func:\n            The function to call. Called without arguments.\n        :param when:\n            The phase in which the function is called.\n        :param reraise:\n            Exception or exceptions that shall propagate if raised by the\n            function, instead of being wrapped in the CallInfo.\n        \"\"\"\n        excinfo = None\n        start = timing.time()\n        precise_start = timing.perf_counter()\n        try:\n>           result: Optional[TResult] = func()\n\ncls        = <class '_pytest.runner.CallInfo'>\nduration   = 0.015315530996304005\nexcinfo    = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_U...arning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n') tblen=7>\nfunc       = <function call_runtest_hook.<locals>.<lambda> at 0x77a6f864bca0>\nprecise_start = 89671.374147393\nprecise_stop = 89671.389462924\nreraise    = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)\nresult     = None\nstart      = 1735791314.99296\nstop       = 1735791315.0082767\nwhen       = 'call'\n\n.venv/lib/python3.9/site-packages/_pytest/runner.py:341: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.9/site-packages/_pytest/runner.py:262: in <lambda>\n    lambda: ihook(item=item, **kwds), when=when, reraise=reraise\n        ihook      = <_HookCaller 'pytest_runtest_call'>\n        item       = <Function test_write_preserves_content_disposition>\n        kwds       = {}\n.venv/lib/python3.9/site-packages/pluggy/_hooks.py:433: in __call__\n    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)\n        firstresult = False\n        kwargs     = {'item': <Function test_write_preserves_content_disposition>}\n        self       = <_HookCaller 'pytest_runtest_call'>\n.venv/lib/python3.9/site-packages/pluggy/_manager.py:112: in _hookexec\n    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)\n        firstresult = False\n        hook_name  = 'pytest_runtest_call'\n        kwargs     = {'item': <Function test_write_preserves_content_disposition>}\n        methods    = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/home/gcpuser/buckets/local_repoeval_bucket/rep...http_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py'>>, ...]\n        self       = <_pytest.config.PytestPluginManager object at 0x77a6fb46fb80>\n.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call\n    yield from unraisable_exception_runtest_hook()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\n    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:\n        with catch_unraisable_exception() as cm:\n            yield\n            if cm.unraisable:\n                if cm.unraisable.err_msg is not None:\n                    err_msg = cm.unraisable.err_msg\n                else:\n                    err_msg = \"Exception ignored in\"\n                msg = f\"{err_msg}: {cm.unraisable.object!r}\\n\\n\"\n                msg += \"\".join(\n                    traceback.format_exception(\n                        cm.unraisable.exc_type,\n                        cm.unraisable.exc_value,\n                        cm.unraisable.exc_traceback,\n                    )\n                )\n>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))\nE               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\nE               \nE               Traceback (most recent call last):\nE                 File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_7eecdff163ccf029fbb1ddc9de4169d4aaeb6597/aiohttp/test_utils.py\", line 537, in teardown_test_loop\nE                   gc.collect()\nE               ResourceWarning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\n\ncm         = <_pytest.unraisableexception.catch_unraisable_exception object at 0x77a6f7d51e20>\nerr_msg    = 'Exception ignored in'\nmsg        = 'Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n\\nTr...nResourceWarning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n'\n\n.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning\n==================================== PASSES ====================================\n\n---------- coverage: platform linux, python 3.9.21-final-0 -----------\nName                                     Stmts   Miss Branch BrPart  Cover\n--------------------------------------------------------------------------\naiohttp/__init__.py                         27      8      2      1    69%\naiohttp/abc.py                              90      8      0      0    91%\naiohttp/base_protocol.py                    67     51     22      0    18%\naiohttp/client.py                          493    372    182      2    19%\naiohttp/client_exceptions.py               135     61     16      0    49%\naiohttp/client_proto.py                    159    129     56      0    14%\naiohttp/client_reqrep.py                   681    508    292      0    18%\naiohttp/client_ws.py                       210    168     66      1    16%\naiohttp/compression_utils.py                70     18     14      2    67%\naiohttp/connector.py                       718    593    309      0    12%\naiohttp/cookiejar.py                       259    205    125      0    14%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         527    304    180     10    36%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     510    379    208      8    20%\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                       583     78    242     27    83%\naiohttp/payload.py                         219     52     56      8    72%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   167     96     42      5    40%\naiohttp/resolver.py                         68     48     22      0    22%\naiohttp/streams.py                         396    222    144     24    38%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      316    196     52      4    34%\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                145    121     48      0    12%\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    285    128      1    28%\naiohttp/web_response.py                    483    370    218      0    16%\naiohttp/web_routedef.py                    106     46     10      0    55%\naiohttp/web_runner.py                      216    133     38      0    33%\naiohttp/web_server.py                       41     22      8      0    39%\naiohttp/web_urldispatcher.py               719    459    161      0    30%\naiohttp/web_ws.py                          335    267    116      1    16%\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           2310   2310     76      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               784    784     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               483    483     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                   1529   1529     76      0     0%\ntests/test_cookiejar.py                    347    347     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                      469    469     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1053   1053     68      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    762    762     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                     165    165     12      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      520    520     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                  839    839     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              1536   1536     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      653    653     18      0     0%\ntests/test_web_server.py                   163    163      0      0     0%\ntests/test_web_urldispatcher.py            367    367     16      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     626    626     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                                    32701  28855   4168     95    11%\n\n============================= slowest 10 durations =============================\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header\n0.02s call     r2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk\n0.02s call     r2e_tests/test_1.py::test_writer_with_content_transfer_encoding_base64\n0.02s call     r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_gzip\n0.02s call     r2e_tests/test_1.py::test_writer_content_transfer_encoding_quote_printable\n0.02s call     r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_deflate\n0.02s call     r2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition\n0.01s call     r2e_tests/test_1.py::TestMultipartResponseWrapper::test_next\n0.01s call     r2e_tests/test_1.py::TestMultipartReader::test_emit_next\n0.01s call     r2e_tests/test_1.py::test_writer_write_no_parts\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_at_eof\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_next\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_release\nPASSED r2e_tests/test_1.py::TestMultipartResponseWrapper::test_release_when_stream_at_eof\nPASSED r2e_tests/test_1.py::TestPartReader::test_next\nPASSED r2e_tests/test_1.py::TestPartReader::test_next_next\nPASSED r2e_tests/test_1.py::TestPartReader::test_read\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_at_eof\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_without_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_incomplete_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_all_at_once\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_incomplete_body_chunked\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_boundary_with_incomplete_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_multi_read_chunk\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_chunk_properly_counts_read_bytes\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_does_not_read_boundary\nPASSED r2e_tests/test_1.py::TestPartReader::test_multiread\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_multiline\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_respects_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_gzip\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_deflate\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_identity\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_encoding_unknown\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_base64\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_quoted_printable\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[binary]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[8bit]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_binary[7bit]\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_with_content_transfer_encoding_unknown\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_default_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_compressed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_text_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_compressed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_json_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_invalid_utf8\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_guess_encoding\nPASSED r2e_tests/test_1.py::TestPartReader::test_read_form_while_closed\nPASSED r2e_tests/test_1.py::TestPartReader::test_readline\nPASSED r2e_tests/test_1.py::TestPartReader::test_release\nPASSED r2e_tests/test_1.py::TestPartReader::test_release_respects_content_length\nPASSED r2e_tests/test_1.py::TestPartReader::test_release_release\nPASSED r2e_tests/test_1.py::TestPartReader::test_filename\nPASSED r2e_tests/test_1.py::TestPartReader::test_reading_long_part\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_from_response\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_bad_boundary\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_bodypart\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_multipart\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_dispatch_custom_multipart_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_emit_next\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_invalid_boundary\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_release\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_next\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_second_next_releases_previous_object\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_release_without_read_the_last_object\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_chunk_by_length_doesnt_breaks_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_chunk_from_stream_doesnt_breaks_reader\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_reading_skips_prelude\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_form_default_encoding\nPASSED r2e_tests/test_1.py::TestMultipartReader::test_read_form_invalid_default_encoding\nPASSED r2e_tests/test_1.py::test_writer\nPASSED r2e_tests/test_1.py::test_writer_serialize_io_chunk\nPASSED r2e_tests/test_1.py::test_writer_serialize_json\nPASSED r2e_tests/test_1.py::test_writer_serialize_form\nPASSED r2e_tests/test_1.py::test_writer_serialize_form_dict\nPASSED r2e_tests/test_1.py::test_writer_write\nPASSED r2e_tests/test_1.py::test_writer_write_no_close_boundary\nPASSED r2e_tests/test_1.py::test_writer_write_no_parts\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_gzip\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_deflate\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_identity\nPASSED r2e_tests/test_1.py::test_writer_serialize_with_content_encoding_unknown\nPASSED r2e_tests/test_1.py::test_writer_with_content_transfer_encoding_base64\nPASSED r2e_tests/test_1.py::test_writer_content_transfer_encoding_quote_printable\nPASSED r2e_tests/test_1.py::test_writer_content_transfer_encoding_unknown\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_default_subtype\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_unquoted_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_quoted_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_bad_boundary\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_default_headers\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_iter_parts\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_with_headers\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_json\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_part\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_json_overrides_content_type\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_form\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_multipart\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_with\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_int_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_float_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_append_none_not_allowed\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_preserve_content_disposition_header\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_override\nPASSED r2e_tests/test_1.py::TestMultipartWriter::test_reset_content_disposition_header\nPASSED r2e_tests/test_1.py::test_async_for_reader\nPASSED r2e_tests/test_1.py::test_async_for_bodypart\nFAILED r2e_tests/test_1.py::TestMultipartWriter::test_set_content_disposition_after_append\nFAILED r2e_tests/test_1.py::TestMultipartWriter::test_write_preserves_content_disposition\n======================== 2 failed, 106 passed in 4.56s =========================\n",
    "old_commit_res_stderr": ""
}
CHANGES/8332.bugfix.rst aiohttp/multipart.py tests/test_multipart.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Content-Disposition Header Not Properly Set After Appending Payload in MultipartWriter

**Description:**
When using `MultipartWriter` to append a payload and set its `Content-Disposition`, the header does not get correctly set, causing related operations to fail.

**Example Buggy Code:**
```python
from aiohttp import MultipartWriter

writer = MultipartWriter("form-data")
payload = writer.append("some-data")
payload.set_content_disposition("form-data", name="method")
# At this point, Content-Disposition is expected to be set in payload.headers
```

In this scenario, after appending the payload and setting the `Content-Disposition`, the headers do not include the `Content-Disposition` field as expected.

**Expected Behavior:**
After setting the `Content-Disposition` on the payload, the `Content-Disposition` header should be present in the payload's headers with the specified parameters. This ensures that the multipart data is correctly formatted and can be processed as intended.

**Actual Behavior:**
The `Content-Disposition` header is missing from `payload.headers`, leading to failures in processes that rely on this header being correctly set.

[/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/multipart.py
import base64
import binascii
import json
import re
import uuid
import warnings
import zlib
from collections import deque
from types import TracebackType
from typing import (
    TYPE_CHECKING,
    Any,
    AsyncIterator,
    Deque,
    Dict,
    Iterator,
    List,
    Mapping,
    Optional,
    Sequence,
    Tuple,
    Type,
    Union,
    cast,
)
from urllib.parse import parse_qsl, unquote, urlencode

from multidict import CIMultiDict, CIMultiDictProxy

from .compression_utils import ZLibCompressor, ZLibDecompressor
from .hdrs import (
    CONTENT_DISPOSITION,
    CONTENT_ENCODING,
    CONTENT_LENGTH,
    CONTENT_TRANSFER_ENCODING,
    CONTENT_TYPE,
)
from .helpers import CHAR, TOKEN, parse_mimetype, reify
from .http import HeadersParser
from .payload import (
    JsonPayload,
    LookupError,
    Order,
    Payload,
    StringPayload,
    get_payload,
    payload_type,
)
from .streams import StreamReader

__all__ = (
    "MultipartReader",
    "MultipartWriter",
    "BodyPartReader",
    "BadContentDispositionHeader",
    "BadContentDispositionParam",
    "parse_content_disposition",
    "content_disposition_filename",
)


if TYPE_CHECKING:
    from .client_reqrep import ClientResponse


class BadContentDispositionHeader(RuntimeWarning):
    pass


class BadContentDispositionParam(RuntimeWarning):
    pass


def parse_content_disposition(
    header: Optional[str],
) -> Tuple[Optional[str], Dict[str, str]]:
    def is_token(string: str) -> bool:
        return bool(string) and TOKEN >= set(string)

    def is_quoted(string: str) -> bool:
        return string[0] == string[-1] == '"'

    def is_rfc5987(string: str) -> bool:
        return is_token(string) and string.count("'") == 2

    def is_extended_param(string: str) -> bool:
        return string.endswith("*")

    def is_continuous_param(string: str) -> bool:
        pos = string.find("*") + 1
        if not pos:
            return False
        substring = string[pos:-1] if string.endswith("*") else string[pos:]
        return substring.isdigit()

    def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str:
        return re.sub(f"\\\\([{chars}])", "\\1", text)

    if not header:
        return None, {}

    disptype, *parts = header.split(";")
    if not is_token(disptype):
        warnings.warn(BadContentDispositionHeader(header))
        return None, {}

    params: Dict[str, str] = {}
    while parts:
        item = parts.pop(0)

        if "=" not in item:
            warnings.warn(BadContentDispositionHeader(header))
            return None, {}

        key, value = item.split("=", 1)
        key = key.lower().strip()
        value = value.lstrip()

        if key in params:
            warnings.warn(BadContentDispositionHeader(header))
            return None, {}

        if not is_token(key):
            warnings.warn(BadContentDispositionParam(item))
            continue

        elif is_continuous_param(key):
            if is_quoted(value):
                value = unescape(value[1:-1])
            elif not is_token(value):
                warnings.warn(BadContentDispositionParam(item))
                continue

        elif is_extended_param(key):
            if is_rfc5987(value):
                encoding, _, value = value.split("'", 2)
                encoding = encoding or "utf-8"
            else:
                warnings.warn(BadContentDispositionParam(item))
                continue

            try:
                value = unquote(value, encoding, "strict")
            except UnicodeDecodeError:  # pragma: nocover
                warnings.warn(BadContentDispositionParam(item))
                continue

        else:
            failed = True
            if is_quoted(value):
                failed = False
                value = unescape(value[1:-1].lstrip("\\/"))
            elif is_token(value):
                failed = False
            elif parts:
                # maybe just ; in filename, in any case this is just
                # one case fix, for proper fix we need to redesign parser
                _value = f"{value};{parts[0]}"
                if is_quoted(_value):
                    parts.pop(0)
                    value = unescape(_value[1:-1].lstrip("\\/"))
                    failed = False

            if failed:
                warnings.warn(BadContentDispositionHeader(header))
                return None, {}

        params[key] = value

    return disptype.lower(), params


def content_disposition_filename(
    params: Mapping[str, str], name: str = "filename"
) -> Optional[str]:
    name_suf = "%s*" % name
    if not params:
        return None
    elif name_suf in params:
        return params[name_suf]
    elif name in params:
        return params[name]
    else:
        parts = []
        fnparams = sorted(
            (key, value) for key, value in params.items() if key.startswith(name_suf)
        )
        for num, (key, value) in enumerate(fnparams):
            _, tail = key.split("*", 1)
            if tail.endswith("*"):
                tail = tail[:-1]
            if tail == str(num):
                parts.append(value)
            else:
                break
        if not parts:
            return None
        value = "".join(parts)
        if "'" in value:
            encoding, _, value = value.split("'", 2)
            encoding = encoding or "utf-8"
            return unquote(value, encoding, "strict")
        return value


class MultipartResponseWrapper:
    """Wrapper around the MultipartReader.

    It takes care about
    underlying connection and close it when it needs in.
    """

    def __init__(
        self,
        resp: "ClientResponse",
        stream: "MultipartReader",
    ) -> None:
        self.resp = resp
        self.stream = stream

    def __aiter__(self) -> "MultipartResponseWrapper":
        return self

    async def __anext__(
        self,
    ) -> Union["MultipartReader", "BodyPartReader"]:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    def at_eof(self) -> bool:
        """Returns True when all response data had been read."""
        return self.resp.content.at_eof()

    async def next(
        self,
    ) -> Optional[Union["MultipartReader", "BodyPartReader"]]:
        """Emits next multipart reader object."""
        item = await self.stream.next()
        if self.stream.at_eof():
            await self.release()
        return item

    async def release(self) -> None:
        """Release the connection gracefully.

        All remaining content is read to the void.
        """
        await self.resp.release()


class BodyPartReader:
    """Multipart reader for single body part."""

    chunk_size = 8192

    def __init__(
        self,
        boundary: bytes,
        headers: "CIMultiDictProxy[str]",
        content: StreamReader,
        *,
        subtype: str = "mixed",
        default_charset: Optional[str] = None,
    ) -> None:
        self.headers = headers
        self._boundary = boundary
        self._content = content
        self._default_charset = default_charset
        self._at_eof = False
        self._is_form_data = subtype == "form-data"
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)
        self._length = int(length) if length is not None else None
        self._read_bytes = 0
        self._unread: Deque[bytes] = deque()
        self._prev_chunk: Optional[bytes] = None
        self._content_eof = 0
        self._cache: Dict[str, Any] = {}

    def __aiter__(self) -> AsyncIterator["BodyPartReader"]:
        return self  # type: ignore[return-value]

    async def __anext__(self) -> bytes:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    async def next(self) -> Optional[bytes]:
        item = await self.read()
        if not item:
            return None
        return item

    async def read(self, *, decode: bool = False) -> bytes:
        """Reads body part data.

        decode: Decodes data following by encoding
                method from Content-Encoding header. If it missed
                data remains untouched
        """
        if self._at_eof:
            return b""
        data = bytearray()
        while not self._at_eof:
            data.extend(await self.read_chunk(self.chunk_size))
        if decode:
            return self.decode(data)
        return data

    async def read_chunk(self, size: int = chunk_size) -> bytes:
        """Reads body part content chunk of the specified size.

        size: chunk size
        """
        if self._at_eof:
            return b""
        if self._length:
            chunk = await self._read_chunk_from_length(size)
        else:
            chunk = await self._read_chunk_from_stream(size)

        self._read_bytes += len(chunk)
        if self._read_bytes == self._length:
            self._at_eof = True
        if self._at_eof:
            clrf = await self._content.readline()
            assert (
                b"\r\n" == clrf
            ), "reader did not read all the data or it is malformed"
        return chunk

    async def _read_chunk_from_length(self, size: int) -> bytes:
        # Reads body part content chunk of the specified size.
        # The body part must has Content-Length header with proper value.
        assert self._length is not None, "Content-Length required for chunked read"
        chunk_size = min(size, self._length - self._read_bytes)
        chunk = await self._content.read(chunk_size)
        if self._content.at_eof():
            self._at_eof = True
        return chunk

    async def _read_chunk_from_stream(self, size: int) -> bytes:
        # Reads content chunk of body part with unknown length.
        # The Content-Length header for body part is not necessary.
        assert (
            size >= len(self._boundary) + 2
        ), "Chunk size must be greater or equal than boundary length + 2"
        first_chunk = self._prev_chunk is None
        if first_chunk:
            self._prev_chunk = await self._content.read(size)

        chunk = await self._content.read(size)
        self._content_eof += int(self._content.at_eof())
        assert self._content_eof < 3, "Reading after EOF"
        assert self._prev_chunk is not None
        window = self._prev_chunk + chunk
        sub = b"\r\n" + self._boundary
        if first_chunk:
            idx = window.find(sub)
        else:
            idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub)))
        if idx >= 0:
            # pushing boundary back to content
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=DeprecationWarning)
                self._content.unread_data(window[idx:])
            if size > idx:
                self._prev_chunk = self._prev_chunk[:idx]
            chunk = window[len(self._prev_chunk) : idx]
            if not chunk:
                self._at_eof = True
        result = self._prev_chunk
        self._prev_chunk = chunk
        return result

    async def readline(self) -> bytes:
        """Reads body part by line by line."""
        if self._at_eof:
            return b""

        if self._unread:
            line = self._unread.popleft()
        else:
            line = await self._content.readline()

        if line.startswith(self._boundary):
            # the very last boundary may not come with \r\n,
            # so set single rules for everyone
            sline = line.rstrip(b"\r\n")
            boundary = self._boundary
            last_boundary = self._boundary + b"--"
            # ensure that we read exactly the boundary, not something alike
            if sline == boundary or sline == last_boundary:
                self._at_eof = True
                self._unread.append(line)
                return b""
        else:
            next_line = await self._content.readline()
            if next_line.startswith(self._boundary):
                line = line[:-2]  # strip CRLF but only once
            self._unread.append(next_line)

        return line

    async def release(self) -> None:
        """Like read(), but reads all the data to the void."""
        if self._at_eof:
            return
        while not self._at_eof:
            await self.read_chunk(self.chunk_size)

    async def text(self, *, encoding: Optional[str] = None) -> str:
        """Like read(), but assumes that body part contains text data."""
        data = await self.read(decode=True)
        # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm
        # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send
        encoding = encoding or self.get_charset(default="utf-8")
        return data.decode(encoding)

    async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]:
        """Like read(), but assumes that body parts contains JSON data."""
        data = await self.read(decode=True)
        if not data:
            return None
        encoding = encoding or self.get_charset(default="utf-8")
        return cast(Dict[str, Any], json.loads(data.decode(encoding)))

    async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:
        """Like read(), but assumes that body parts contain form urlencoded data."""
        data = await self.read(decode=True)
        if not data:
            return []
        if encoding is not None:
            real_encoding = encoding
        else:
            real_encoding = self.get_charset(default="utf-8")
        try:
            decoded_data = data.rstrip().decode(real_encoding)
        except UnicodeDecodeError:
            raise ValueError("data cannot be decoded with %s encoding" % real_encoding)

        return parse_qsl(
            decoded_data,
            keep_blank_values=True,
            encoding=real_encoding,
        )

    def at_eof(self) -> bool:
        """Returns True if the boundary was reached or False otherwise."""
        return self._at_eof

    def decode(self, data: bytes) -> bytes:
        """Decodes data.

        Decoding is done according the specified Content-Encoding
        or Content-Transfer-Encoding headers value.
        """
        if CONTENT_TRANSFER_ENCODING in self.headers:
            data = self._decode_content_transfer(data)
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
        if not self._is_form_data and CONTENT_ENCODING in self.headers:
            return self._decode_content(data)
        return data

    def _decode_content(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_ENCODING, "").lower()
        if encoding == "identity":
            return data
        if encoding in {"deflate", "gzip"}:
            return ZLibDecompressor(
                encoding=encoding,
                suppress_deflate_header=True,
            ).decompress_sync(data)

        raise RuntimeError(f"unknown content encoding: {encoding}")

    def _decode_content_transfer(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower()

        if encoding == "base64":
            return base64.b64decode(data)
        elif encoding == "quoted-printable":
            return binascii.a2b_qp(data)
        elif encoding in ("binary", "8bit", "7bit"):
            return data
        else:
            raise RuntimeError(
                "unknown content transfer encoding: {}" "".format(encoding)
            )

    def get_charset(self, default: str) -> str:
        """Returns charset parameter from Content-Type header or default."""
        ctype = self.headers.get(CONTENT_TYPE, "")
        mimetype = parse_mimetype(ctype)
        return mimetype.parameters.get("charset", self._default_charset or default)

    @reify
    def name(self) -> Optional[str]:
        """Returns name specified in Content-Disposition header.

        If the header is missing or malformed, returns None.
        """
        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
        return content_disposition_filename(params, "name")

    @reify
    def filename(self) -> Optional[str]:
        """Returns filename specified in Content-Disposition header.

        Returns None if the header is missing or malformed.
        """
        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
        return content_disposition_filename(params, "filename")


@payload_type(BodyPartReader, order=Order.try_first)
class BodyPartReaderPayload(Payload):
    def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:
        super().__init__(value, *args, **kwargs)

        params: Dict[str, str] = {}
        if value.name is not None:
            params["name"] = value.name
        if value.filename is not None:
            params["filename"] = value.filename

        if params:
            self.set_content_disposition("attachment", True, **params)

    async def write(self, writer: Any) -> None:
        field = self._value
        chunk = await field.read_chunk(size=2**16)
        while chunk:
            await writer.write(field.decode(chunk))
            chunk = await field.read_chunk(size=2**16)


class MultipartReader:
    """Multipart body reader."""

    #: Response wrapper, used when multipart readers constructs from response.
    response_wrapper_cls = MultipartResponseWrapper
    #: Multipart reader class, used to handle multipart/* body parts.
    #: None points to type(self)
    multipart_reader_cls = None
    #: Body part reader class for non multipart/* content types.
    part_reader_cls = BodyPartReader

    def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None:
        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])
        assert self._mimetype.type == "multipart", "multipart/* content type expected"
        if "boundary" not in self._mimetype.parameters:
            raise ValueError(
                "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE]
            )

        self.headers = headers
        self._boundary = ("--" + self._get_boundary()).encode()
        self._content = content
        self._default_charset: Optional[str] = None
        self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None
        self._at_eof = False
        self._at_bof = True
        self._unread: List[bytes] = []

    def __aiter__(
        self,
    ) -> AsyncIterator["BodyPartReader"]:
        return self  # type: ignore[return-value]

    async def __anext__(
        self,
    ) -> Optional[Union["MultipartReader", BodyPartReader]]:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    @classmethod
    def from_response(
        cls,
        response: "ClientResponse",
    ) -> MultipartResponseWrapper:
        """Constructs reader instance from HTTP response.

        :param response: :class:`~aiohttp.client.ClientResponse` instance
        """
        obj = cls.response_wrapper_cls(
            response, cls(response.headers, response.content)
        )
        return obj

    def at_eof(self) -> bool:
        """Returns True if the final boundary was reached, false otherwise."""
        return self._at_eof

    async def next(
        self,
    ) -> Optional[Union["MultipartReader", BodyPartReader]]:
        """Emits the next multipart body part."""
        # So, if we're at BOF, we need to skip till the boundary.
        if self._at_eof:
            return None
        await self._maybe_release_last_part()
        if self._at_bof:
            await self._read_until_first_boundary()
            self._at_bof = False
        else:
            await self._read_boundary()
        if self._at_eof:  # we just read the last boundary, nothing to do there
            return None

        part = await self.fetch_next_part()
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6
        if (
            self._last_part is None
            and self._mimetype.subtype == "form-data"
            and isinstance(part, BodyPartReader)
        ):
            _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION))
            if params.get("name") == "_charset_":
                # Longest encoding in https://encoding.spec.whatwg.org/encodings.json
                # is 19 characters, so 32 should be more than enough for any valid encoding.
                charset = await part.read_chunk(32)
                if len(charset) > 31:
                    raise RuntimeError("Invalid default charset")
                self._default_charset = charset.strip().decode()
                part = await self.fetch_next_part()
        self._last_part = part
        return self._last_part

    async def release(self) -> None:
        """Reads all the body parts to the void till the final boundary."""
        while not self._at_eof:
            item = await self.next()
            if item is None:
                break
            await item.release()

    async def fetch_next_part(
        self,
    ) -> Union["MultipartReader", BodyPartReader]:
        """Returns the next body part reader."""
        headers = await self._read_headers()
        return self._get_part_reader(headers)

    def _get_part_reader(
        self,
        headers: "CIMultiDictProxy[str]",
    ) -> Union["MultipartReader", BodyPartReader]:
        """Dispatches the response by the `Content-Type` header.

        Returns a suitable reader instance.

        :param dict headers: Response headers
        """
        ctype = headers.get(CONTENT_TYPE, "")
        mimetype = parse_mimetype(ctype)

        if mimetype.type == "multipart":
            if self.multipart_reader_cls is None:
                return type(self)(headers, self._content)
            return self.multipart_reader_cls(headers, self._content)
        else:
            return self.part_reader_cls(
                self._boundary,
                headers,
                self._content,
                subtype=self._mimetype.subtype,
                default_charset=self._default_charset,
            )

    def _get_boundary(self) -> str:
        boundary = self._mimetype.parameters["boundary"]
        if len(boundary) > 70:
            raise ValueError("boundary %r is too long (70 chars max)" % boundary)

        return boundary

    async def _readline(self) -> bytes:
        if self._unread:
            return self._unread.pop()
        return await self._content.readline()

    async def _read_until_first_boundary(self) -> None:
        while True:
            chunk = await self._readline()
            if chunk == b"":
                raise ValueError(
                    "Could not find starting boundary %r" % (self._boundary)
                )
            chunk = chunk.rstrip()
            if chunk == self._boundary:
                return
            elif chunk == self._boundary + b"--":
                self._at_eof = True
                return

    async def _read_boundary(self) -> None:
        chunk = (await self._readline()).rstrip()
        if chunk == self._boundary:
            pass
        elif chunk == self._boundary + b"--":
            self._at_eof = True
            epilogue = await self._readline()
            next_line = await self._readline()

            # the epilogue is expected and then either the end of input or the
            # parent multipart boundary, if the parent boundary is found then
            # it should be marked as unread and handed to the parent for
            # processing
            if next_line[:2] == b"--":
                self._unread.append(next_line)
            # otherwise the request is likely missing an epilogue and both
            # lines should be passed to the parent for processing
            # (this handles the old behavior gracefully)
            else:
                self._unread.extend([next_line, epilogue])
        else:
            raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}")

    async def _read_headers(self) -> "CIMultiDictProxy[str]":
        lines = [b""]
        while True:
            chunk = await self._content.readline()
            chunk = chunk.strip()
            lines.append(chunk)
            if not chunk:
                break
        parser = HeadersParser()
        headers, raw_headers = parser.parse_headers(lines)
        return headers

    async def _maybe_release_last_part(self) -> None:
        """Ensures that the last read body part is read completely."""
        if self._last_part is not None:
            if not self._last_part.at_eof():
                await self._last_part.release()
            self._unread.extend(self._last_part._unread)
            self._last_part = None


_Part = Tuple[Payload, str, str]


class MultipartWriter(Payload):
    """Multipart body writer."""

    def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None:
        boundary = boundary if boundary is not None else uuid.uuid4().hex
        # The underlying Payload API demands a str (utf-8), not bytes,
        # so we need to ensure we don't lose anything during conversion.
        # As a result, require the boundary to be ASCII only.
        # In both situations.

        try:
            self._boundary = boundary.encode("ascii")
        except UnicodeEncodeError:
            raise ValueError("boundary should contain ASCII only chars") from None
        ctype = f"multipart/{subtype}; boundary={self._boundary_value}"

        super().__init__(None, content_type=ctype)

        self._parts: List[_Part] = []
        self._is_form_data = subtype == "form-data"

    def __enter__(self) -> "MultipartWriter":
        return self

    def __exit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> None:
        pass

    def __iter__(self) -> Iterator[_Part]:
        return iter(self._parts)

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

    def __bool__(self) -> bool:
        return True

    _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z")
    _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]")

    @property
    def _boundary_value(self) -> str:
        """Wrap boundary parameter value in quotes, if necessary.

        Reads self.boundary and returns a unicode string.
        """
        # Refer to RFCs 7231, 7230, 5234.
        #
        # parameter      = token "=" ( token / quoted-string )
        # token          = 1*tchar
        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
        # obs-text       = %x80-FF
        # quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )
        # tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
        #                  / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
        #                  / DIGIT / ALPHA
        #                  ; any VCHAR, except delimiters
        # VCHAR           = %x21-7E
        value = self._boundary
        if re.match(self._valid_tchar_regex, value):
            return value.decode("ascii")  # cannot fail

        if re.search(self._invalid_qdtext_char_regex, value):
            raise ValueError("boundary value contains invalid characters")

        # escape %x5C and %x22
        quoted_value_content = value.replace(b"\\", b"\\\\")
        quoted_value_content = quoted_value_content.replace(b'"', b'\\"')

        return '"' + quoted_value_content.decode("ascii") + '"'

    @property
    def boundary(self) -> str:
        return self._boundary.decode("ascii")

    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:
        if headers is None:
            headers = CIMultiDict()

        if isinstance(obj, Payload):
            obj.headers.update(headers)
            return self.append_payload(obj)
        else:
            try:
                payload = get_payload(obj, headers=headers)
            except LookupError:
                raise TypeError("Cannot create payload from %r" % obj)
            else:
                return self.append_payload(payload)

    def append_payload(self, payload: Payload) -> Payload:
        """Adds a new body part to multipart writer."""
        encoding: Optional[str] = None
        te_encoding: Optional[str] = None
        if self._is_form_data:
            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7
            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
            assert CONTENT_DISPOSITION in payload.headers
            assert "name=" in payload.headers[CONTENT_DISPOSITION]
            assert (
                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}
                & payload.headers.keys()
            )
        else:
            # compression
            encoding = payload.headers.get(CONTENT_ENCODING, "").lower()
            if encoding and encoding not in ("deflate", "gzip", "identity"):
                raise RuntimeError(f"unknown content encoding: {encoding}")
            if encoding == "identity":
                encoding = None

            # te encoding
            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, "").lower()
            if te_encoding not in ("", "base64", "quoted-printable", "binary"):
                raise RuntimeError(f"unknown content transfer encoding: {te_encoding}")
            if te_encoding == "binary":
                te_encoding = None

            # size
            size = payload.size
            if size is not None and not (encoding or te_encoding):
                payload.headers[CONTENT_LENGTH] = str(size)

        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]
        return payload

    def append_json(
        self, obj: Any, headers: Optional[Mapping[str, str]] = None
    ) -> Payload:
        """Helper to append JSON part."""
        if headers is None:
            headers = CIMultiDict()

        return self.append_payload(JsonPayload(obj, headers=headers))

    def append_form(
        self,
        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],
        headers: Optional[Mapping[str, str]] = None,
    ) -> Payload:
        """Helper to append form urlencoded part."""
        assert isinstance(obj, (Sequence, Mapping))

        if headers is None:
            headers = CIMultiDict()

        if isinstance(obj, Mapping):
            obj = list(obj.items())
        data = urlencode(obj, doseq=True)

        return self.append_payload(
            StringPayload(
                data, headers=headers, content_type="application/x-www-form-urlencoded"
            )
        )

    @property
    def size(self) -> Optional[int]:
        """Size of the payload."""
        total = 0
        for part, encoding, te_encoding in self._parts:
            if encoding or te_encoding or part.size is None:
                return None

            total += int(
                2
                + len(self._boundary)
                + 2
                + part.size  # b'--'+self._boundary+b'\r\n'
                + len(part._binary_headers)
                + 2  # b'\r\n'
            )

        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\r\n'
        return total

    async def write(self, writer: Any, close_boundary: bool = True) -> None:
        """Write body."""
        for part, encoding, te_encoding in self._parts:
            await writer.write(b"--" + self._boundary + b"\r\n")
            await writer.write(part._binary_headers)

            if encoding or te_encoding:
                w = MultipartPayloadWriter(writer)
                if encoding:
                    w.enable_compression(encoding)
                if te_encoding:
                    w.enable_encoding(te_encoding)
                await part.write(w)  # type: ignore[arg-type]
                await w.write_eof()
            else:
                await part.write(writer)

            await writer.write(b"\r\n")

        if close_boundary:
            await writer.write(b"--" + self._boundary + b"--\r\n")


class MultipartPayloadWriter:
    def __init__(self, writer: Any) -> None:
        self._writer = writer
        self._encoding: Optional[str] = None
        self._compress: Optional[ZLibCompressor] = None
        self._encoding_buffer: Optional[bytearray] = None

    def enable_encoding(self, encoding: str) -> None:
        if encoding == "base64":
            self._encoding = encoding
            self._encoding_buffer = bytearray()
        elif encoding == "quoted-printable":
            self._encoding = "quoted-printable"

    def enable_compression(
        self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY
    ) -> None:
        self._compress = ZLibCompressor(
            encoding=encoding,
            suppress_deflate_header=True,
            strategy=strategy,
        )

    async def write_eof(self) -> None:
        if self._compress is not None:
            chunk = self._compress.flush()
            if chunk:
                self._compress = None
                await self.write(chunk)

        if self._encoding == "base64":
            if self._encoding_buffer:
                await self._writer.write(base64.b64encode(self._encoding_buffer))

    async def write(self, chunk: bytes) -> None:
        if self._compress is not None:
            if chunk:
                chunk = await self._compress.compress(chunk)
                if not chunk:
                    return

        if self._encoding == "base64":
            buf = self._encoding_buffer
            assert buf is not None
            buf.extend(chunk)

            if buf:
                div, mod = divmod(len(buf), 3)
                enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :])
                if enc_chunk:
                    b64chunk = base64.b64encode(enc_chunk)
                    await self._writer.write(b64chunk)
        elif self._encoding == "quoted-printable":
            await self._writer.write(binascii.b2a_qp(chunk))
        else:
            await self._writer.write(chunk)

```
--- 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:** Content-Disposition Header Not Properly Set After Appending Payload in MultipartWriter

**Description:**
When using `MultipartWriter` to append a payload and set its `Content-Disposition`, the header does not get correctly set, causing related operations to fail.

**Example Buggy Code:**
```python
from aiohttp import MultipartWriter

writer = MultipartWriter("form-data")
payload = writer.append("some-data")
payload.set_content_disposition("form-data", name="method")
# At this point, Content-Disposition is expected to be set in payload.headers
```

In this scenario, after appending the payload and setting the `Content-Disposition`, the headers do not include the `Content-Disposition` field as expected.

**Expected Behavior:**
After setting the `Content-Disposition` on the payload, the `Content-Disposition` header should be present in the payload's headers with the specified parameters. This ensures that the multipart data is correctly formatted and can be processed as intended.

**Actual Behavior:**
The `Content-Disposition` header is missing from `payload.headers`, leading to failures in processes that rely on this header being correctly set.

[/ISSUE]
{
    "TestMultipartResponseWrapper.test_at_eof": "PASSED",
    "TestMultipartResponseWrapper.test_next": "PASSED",
    "TestMultipartResponseWrapper.test_release": "PASSED",
    "TestMultipartResponseWrapper.test_release_when_stream_at_eof": "PASSED",
    "TestPartReader.test_next": "PASSED",
    "TestPartReader.test_next_next": "PASSED",
    "TestPartReader.test_read": "PASSED",
    "TestPartReader.test_read_chunk_at_eof": "PASSED",
    "TestPartReader.test_read_chunk_without_content_length": "PASSED",
    "TestPartReader.test_read_incomplete_chunk": "PASSED",
    "TestPartReader.test_read_all_at_once": "PASSED",
    "TestPartReader.test_read_incomplete_body_chunked": "PASSED",
    "TestPartReader.test_read_boundary_with_incomplete_chunk": "PASSED",
    "TestPartReader.test_multi_read_chunk": "PASSED",
    "TestPartReader.test_read_chunk_properly_counts_read_bytes": "PASSED",
    "TestPartReader.test_read_does_not_read_boundary": "PASSED",
    "TestPartReader.test_multiread": "PASSED",
    "TestPartReader.test_read_multiline": "PASSED",
    "TestPartReader.test_read_respects_content_length": "PASSED",
    "TestPartReader.test_read_with_content_encoding_gzip": "PASSED",
    "TestPartReader.test_read_with_content_encoding_deflate": "PASSED",
    "TestPartReader.test_read_with_content_encoding_identity": "PASSED",
    "TestPartReader.test_read_with_content_encoding_unknown": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_base64": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_quoted_printable": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_binary[binary]": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_binary[8bit]": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_binary[7bit]": "PASSED",
    "TestPartReader.test_read_with_content_transfer_encoding_unknown": "PASSED",
    "TestPartReader.test_read_text": "PASSED",
    "TestPartReader.test_read_text_default_encoding": "PASSED",
    "TestPartReader.test_read_text_encoding": "PASSED",
    "TestPartReader.test_read_text_guess_encoding": "PASSED",
    "TestPartReader.test_read_text_compressed": "PASSED",
    "TestPartReader.test_read_text_while_closed": "PASSED",
    "TestPartReader.test_read_json": "PASSED",
    "TestPartReader.test_read_json_encoding": "PASSED",
    "TestPartReader.test_read_json_guess_encoding": "PASSED",
    "TestPartReader.test_read_json_compressed": "PASSED",
    "TestPartReader.test_read_json_while_closed": "PASSED",
    "TestPartReader.test_read_form": "PASSED",
    "TestPartReader.test_read_form_invalid_utf8": "PASSED",
    "TestPartReader.test_read_form_encoding": "PASSED",
    "TestPartReader.test_read_form_guess_encoding": "PASSED",
    "TestPartReader.test_read_form_while_closed": "PASSED",
    "TestPartReader.test_readline": "PASSED",
    "TestPartReader.test_release": "PASSED",
    "TestPartReader.test_release_respects_content_length": "PASSED",
    "TestPartReader.test_release_release": "PASSED",
    "TestPartReader.test_filename": "PASSED",
    "TestPartReader.test_reading_long_part": "PASSED",
    "TestMultipartReader.test_from_response": "PASSED",
    "TestMultipartReader.test_bad_boundary": "PASSED",
    "TestMultipartReader.test_dispatch": "PASSED",
    "TestMultipartReader.test_dispatch_bodypart": "PASSED",
    "TestMultipartReader.test_dispatch_multipart": "PASSED",
    "TestMultipartReader.test_dispatch_custom_multipart_reader": "PASSED",
    "TestMultipartReader.test_emit_next": "PASSED",
    "TestMultipartReader.test_invalid_boundary": "PASSED",
    "TestMultipartReader.test_release": "PASSED",
    "TestMultipartReader.test_release_release": "PASSED",
    "TestMultipartReader.test_release_next": "PASSED",
    "TestMultipartReader.test_second_next_releases_previous_object": "PASSED",
    "TestMultipartReader.test_release_without_read_the_last_object": "PASSED",
    "TestMultipartReader.test_read_chunk_by_length_doesnt_breaks_reader": "PASSED",
    "TestMultipartReader.test_read_chunk_from_stream_doesnt_breaks_reader": "PASSED",
    "TestMultipartReader.test_reading_skips_prelude": "PASSED",
    "TestMultipartReader.test_read_form_default_encoding": "PASSED",
    "TestMultipartReader.test_read_form_invalid_default_encoding": "PASSED",
    "test_writer": "PASSED",
    "test_writer_serialize_io_chunk": "PASSED",
    "test_writer_serialize_json": "PASSED",
    "test_writer_serialize_form": "PASSED",
    "test_writer_serialize_form_dict": "PASSED",
    "test_writer_write": "PASSED",
    "test_writer_write_no_close_boundary": "PASSED",
    "test_writer_write_no_parts": "PASSED",
    "test_writer_serialize_with_content_encoding_gzip": "PASSED",
    "test_writer_serialize_with_content_encoding_deflate": "PASSED",
    "test_writer_serialize_with_content_encoding_identity": "PASSED",
    "test_writer_serialize_with_content_encoding_unknown": "PASSED",
    "test_writer_with_content_transfer_encoding_base64": "PASSED",
    "test_writer_content_transfer_encoding_quote_printable": "PASSED",
    "test_writer_content_transfer_encoding_unknown": "PASSED",
    "TestMultipartWriter.test_default_subtype": "PASSED",
    "TestMultipartWriter.test_unquoted_boundary": "PASSED",
    "TestMultipartWriter.test_quoted_boundary": "PASSED",
    "TestMultipartWriter.test_bad_boundary": "PASSED",
    "TestMultipartWriter.test_default_headers": "PASSED",
    "TestMultipartWriter.test_iter_parts": "PASSED",
    "TestMultipartWriter.test_append": "PASSED",
    "TestMultipartWriter.test_append_with_headers": "PASSED",
    "TestMultipartWriter.test_append_json": "PASSED",
    "TestMultipartWriter.test_append_part": "PASSED",
    "TestMultipartWriter.test_append_json_overrides_content_type": "PASSED",
    "TestMultipartWriter.test_append_form": "PASSED",
    "TestMultipartWriter.test_append_multipart": "PASSED",
    "TestMultipartWriter.test_set_content_disposition_after_append": "PASSED",
    "TestMultipartWriter.test_with": "PASSED",
    "TestMultipartWriter.test_append_int_not_allowed": "PASSED",
    "TestMultipartWriter.test_append_float_not_allowed": "PASSED",
    "TestMultipartWriter.test_append_none_not_allowed": "PASSED",
    "TestMultipartWriter.test_write_preserves_content_disposition": "PASSED",
    "TestMultipartWriter.test_preserve_content_disposition_header": "PASSED",
    "TestMultipartWriter.test_set_content_disposition_override": "PASSED",
    "TestMultipartWriter.test_reset_content_disposition_header": "PASSED",
    "test_async_for_reader": "PASSED",
    "test_async_for_bodypart": "PASSED"
}
diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py
index a43ec545..fcdf1618 100644
--- a/aiohttp/multipart.py
+++ b/aiohttp/multipart.py
@@ -848,8 +848,6 @@ class MultipartWriter(Payload):
         if self._is_form_data:
             # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7
             # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
-            assert CONTENT_DISPOSITION in payload.headers
-            assert "name=" in payload.headers[CONTENT_DISPOSITION]
             assert (
                 not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}
                 & payload.headers.keys()
@@ -930,6 +928,11 @@ class MultipartWriter(Payload):
     async def write(self, writer: Any, close_boundary: bool = True) -> None:
         """Write body."""
         for part, encoding, te_encoding in self._parts:
+            if self._is_form_data:
+                # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
+                assert CONTENT_DISPOSITION in part.headers
+                assert "name=" in part.headers[CONTENT_DISPOSITION]
+
             await writer.write(b"--" + self._boundary + b"\r\n")
             await writer.write(part._binary_headers)
 

{"aiohttp/multipart.py": "import base64\nimport binascii\nimport json\nimport re\nimport uuid\nimport warnings\nimport zlib\nfrom collections import deque\nfrom types import TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Deque,\n    Dict,\n    Iterator,\n    List,\n    Mapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\nfrom urllib.parse import parse_qsl, unquote, urlencode\n\nfrom multidict import CIMultiDict, CIMultiDictProxy\n\nfrom .compression_utils import ZLibCompressor, ZLibDecompressor\nfrom .hdrs import (\n    CONTENT_DISPOSITION,\n    CONTENT_ENCODING,\n    CONTENT_LENGTH,\n    CONTENT_TRANSFER_ENCODING,\n    CONTENT_TYPE,\n)\nfrom .helpers import CHAR, TOKEN, parse_mimetype, reify\nfrom .http import HeadersParser\nfrom .payload import (\n    JsonPayload,\n    LookupError,\n    Order,\n    Payload,\n    StringPayload,\n    get_payload,\n    payload_type,\n)\nfrom .streams import StreamReader\n\n__all__ = (\n    \"MultipartReader\",\n    \"MultipartWriter\",\n    \"BodyPartReader\",\n    \"BadContentDispositionHeader\",\n    \"BadContentDispositionParam\",\n    \"parse_content_disposition\",\n    \"content_disposition_filename\",\n)\n\n\nif TYPE_CHECKING:\n    from .client_reqrep import ClientResponse\n\n\nclass BadContentDispositionHeader(RuntimeWarning):\n    pass\n\n\nclass BadContentDispositionParam(RuntimeWarning):\n    pass\n\n\ndef parse_content_disposition(\n    header: Optional[str],\n) -> Tuple[Optional[str], Dict[str, str]]:\n    def is_token(string: str) -> bool:\n        return bool(string) and TOKEN >= set(string)\n\n    def is_quoted(string: str) -> bool:\n        return string[0] == string[-1] == '\"'\n\n    def is_rfc5987(string: str) -> bool:\n        return is_token(string) and string.count(\"'\") == 2\n\n    def is_extended_param(string: str) -> bool:\n        return string.endswith(\"*\")\n\n    def is_continuous_param(string: str) -> bool:\n        pos = string.find(\"*\") + 1\n        if not pos:\n            return False\n        substring = string[pos:-1] if string.endswith(\"*\") else string[pos:]\n        return substring.isdigit()\n\n    def unescape(text: str, *, chars: str = \"\".join(map(re.escape, CHAR))) -> str:\n        return re.sub(f\"\\\\\\\\([{chars}])\", \"\\\\1\", text)\n\n    if not header:\n        return None, {}\n\n    disptype, *parts = header.split(\";\")\n    if not is_token(disptype):\n        warnings.warn(BadContentDispositionHeader(header))\n        return None, {}\n\n    params: Dict[str, str] = {}\n    while parts:\n        item = parts.pop(0)\n\n        if \"=\" not in item:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        key, value = item.split(\"=\", 1)\n        key = key.lower().strip()\n        value = value.lstrip()\n\n        if key in params:\n            warnings.warn(BadContentDispositionHeader(header))\n            return None, {}\n\n        if not is_token(key):\n            warnings.warn(BadContentDispositionParam(item))\n            continue\n\n        elif is_continuous_param(key):\n            if is_quoted(value):\n                value = unescape(value[1:-1])\n            elif not is_token(value):\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        elif is_extended_param(key):\n            if is_rfc5987(value):\n                encoding, _, value = value.split(\"'\", 2)\n                encoding = encoding or \"utf-8\"\n            else:\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n            try:\n                value = unquote(value, encoding, \"strict\")\n            except UnicodeDecodeError:  # pragma: nocover\n                warnings.warn(BadContentDispositionParam(item))\n                continue\n\n        else:\n            failed = True\n            if is_quoted(value):\n                failed = False\n                value = unescape(value[1:-1].lstrip(\"\\\\/\"))\n            elif is_token(value):\n                failed = False\n            elif parts:\n                # maybe just ; in filename, in any case this is just\n                # one case fix, for proper fix we need to redesign parser\n                _value = f\"{value};{parts[0]}\"\n                if is_quoted(_value):\n                    parts.pop(0)\n                    value = unescape(_value[1:-1].lstrip(\"\\\\/\"))\n                    failed = False\n\n            if failed:\n                warnings.warn(BadContentDispositionHeader(header))\n                return None, {}\n\n        params[key] = value\n\n    return disptype.lower(), params\n\n\ndef content_disposition_filename(\n    params: Mapping[str, str], name: str = \"filename\"\n) -> Optional[str]:\n    name_suf = \"%s*\" % name\n    if not params:\n        return None\n    elif name_suf in params:\n        return params[name_suf]\n    elif name in params:\n        return params[name]\n    else:\n        parts = []\n        fnparams = sorted(\n            (key, value) for key, value in params.items() if key.startswith(name_suf)\n        )\n        for num, (key, value) in enumerate(fnparams):\n            _, tail = key.split(\"*\", 1)\n            if tail.endswith(\"*\"):\n                tail = tail[:-1]\n            if tail == str(num):\n                parts.append(value)\n            else:\n                break\n        if not parts:\n            return None\n        value = \"\".join(parts)\n        if \"'\" in value:\n            encoding, _, value = value.split(\"'\", 2)\n            encoding = encoding or \"utf-8\"\n            return unquote(value, encoding, \"strict\")\n        return value\n\n\nclass MultipartResponseWrapper:\n    \"\"\"Wrapper around the MultipartReader.\n\n    It takes care about\n    underlying connection and close it when it needs in.\n    \"\"\"\n\n    def __init__(\n        self,\n        resp: \"ClientResponse\",\n        stream: \"MultipartReader\",\n    ) -> None:\n        self.resp = resp\n        self.stream = stream\n\n    def __aiter__(self) -> \"MultipartResponseWrapper\":\n        return self\n\n    async def __anext__(\n        self,\n    ) -> Union[\"MultipartReader\", \"BodyPartReader\"]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True when all response data had been read.\"\"\"\n        return self.resp.content.at_eof()\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", \"BodyPartReader\"]]:\n        \"\"\"Emits next multipart reader object.\"\"\"\n        item = await self.stream.next()\n        if self.stream.at_eof():\n            await self.release()\n        return item\n\n    async def release(self) -> None:\n        \"\"\"Release the connection gracefully.\n\n        All remaining content is read to the void.\n        \"\"\"\n        await self.resp.release()\n\n\nclass BodyPartReader:\n    \"\"\"Multipart reader for single body part.\"\"\"\n\n    chunk_size = 8192\n\n    def __init__(\n        self,\n        boundary: bytes,\n        headers: \"CIMultiDictProxy[str]\",\n        content: StreamReader,\n        *,\n        subtype: str = \"mixed\",\n        default_charset: Optional[str] = None,\n    ) -> None:\n        self.headers = headers\n        self._boundary = boundary\n        self._content = content\n        self._default_charset = default_charset\n        self._at_eof = False\n        self._is_form_data = subtype == \"form-data\"\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)\n        self._length = int(length) if length is not None else None\n        self._read_bytes = 0\n        self._unread: Deque[bytes] = deque()\n        self._prev_chunk: Optional[bytes] = None\n        self._content_eof = 0\n        self._cache: Dict[str, Any] = {}\n\n    def __aiter__(self) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(self) -> bytes:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    async def next(self) -> Optional[bytes]:\n        item = await self.read()\n        if not item:\n            return None\n        return item\n\n    async def read(self, *, decode: bool = False) -> bytes:\n        \"\"\"Reads body part data.\n\n        decode: Decodes data following by encoding\n                method from Content-Encoding header. If it missed\n                data remains untouched\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        data = bytearray()\n        while not self._at_eof:\n            data.extend(await self.read_chunk(self.chunk_size))\n        if decode:\n            return self.decode(data)\n        return data\n\n    async def read_chunk(self, size: int = chunk_size) -> bytes:\n        \"\"\"Reads body part content chunk of the specified size.\n\n        size: chunk size\n        \"\"\"\n        if self._at_eof:\n            return b\"\"\n        if self._length:\n            chunk = await self._read_chunk_from_length(size)\n        else:\n            chunk = await self._read_chunk_from_stream(size)\n\n        self._read_bytes += len(chunk)\n        if self._read_bytes == self._length:\n            self._at_eof = True\n        if self._at_eof:\n            clrf = await self._content.readline()\n            assert (\n                b\"\\r\\n\" == clrf\n            ), \"reader did not read all the data or it is malformed\"\n        return chunk\n\n    async def _read_chunk_from_length(self, size: int) -> bytes:\n        # Reads body part content chunk of the specified size.\n        # The body part must has Content-Length header with proper value.\n        assert self._length is not None, \"Content-Length required for chunked read\"\n        chunk_size = min(size, self._length - self._read_bytes)\n        chunk = await self._content.read(chunk_size)\n        if self._content.at_eof():\n            self._at_eof = True\n        return chunk\n\n    async def _read_chunk_from_stream(self, size: int) -> bytes:\n        # Reads content chunk of body part with unknown length.\n        # The Content-Length header for body part is not necessary.\n        assert (\n            size >= len(self._boundary) + 2\n        ), \"Chunk size must be greater or equal than boundary length + 2\"\n        first_chunk = self._prev_chunk is None\n        if first_chunk:\n            self._prev_chunk = await self._content.read(size)\n\n        chunk = await self._content.read(size)\n        self._content_eof += int(self._content.at_eof())\n        assert self._content_eof < 3, \"Reading after EOF\"\n        assert self._prev_chunk is not None\n        window = self._prev_chunk + chunk\n        sub = b\"\\r\\n\" + self._boundary\n        if first_chunk:\n            idx = window.find(sub)\n        else:\n            idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub)))\n        if idx >= 0:\n            # pushing boundary back to content\n            with warnings.catch_warnings():\n                warnings.filterwarnings(\"ignore\", category=DeprecationWarning)\n                self._content.unread_data(window[idx:])\n            if size > idx:\n                self._prev_chunk = self._prev_chunk[:idx]\n            chunk = window[len(self._prev_chunk) : idx]\n            if not chunk:\n                self._at_eof = True\n        result = self._prev_chunk\n        self._prev_chunk = chunk\n        return result\n\n    async def readline(self) -> bytes:\n        \"\"\"Reads body part by line by line.\"\"\"\n        if self._at_eof:\n            return b\"\"\n\n        if self._unread:\n            line = self._unread.popleft()\n        else:\n            line = await self._content.readline()\n\n        if line.startswith(self._boundary):\n            # the very last boundary may not come with \\r\\n,\n            # so set single rules for everyone\n            sline = line.rstrip(b\"\\r\\n\")\n            boundary = self._boundary\n            last_boundary = self._boundary + b\"--\"\n            # ensure that we read exactly the boundary, not something alike\n            if sline == boundary or sline == last_boundary:\n                self._at_eof = True\n                self._unread.append(line)\n                return b\"\"\n        else:\n            next_line = await self._content.readline()\n            if next_line.startswith(self._boundary):\n                line = line[:-2]  # strip CRLF but only once\n            self._unread.append(next_line)\n\n        return line\n\n    async def release(self) -> None:\n        \"\"\"Like read(), but reads all the data to the void.\"\"\"\n        if self._at_eof:\n            return\n        while not self._at_eof:\n            await self.read_chunk(self.chunk_size)\n\n    async def text(self, *, encoding: Optional[str] = None) -> str:\n        \"\"\"Like read(), but assumes that body part contains text data.\"\"\"\n        data = await self.read(decode=True)\n        # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm\n        # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return data.decode(encoding)\n\n    async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]:\n        \"\"\"Like read(), but assumes that body parts contains JSON data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return None\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return cast(Dict[str, Any], json.loads(data.decode(encoding)))\n\n    async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:\n        \"\"\"Like read(), but assumes that body parts contain form urlencoded data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return []\n        if encoding is not None:\n            real_encoding = encoding\n        else:\n            real_encoding = self.get_charset(default=\"utf-8\")\n        try:\n            decoded_data = data.rstrip().decode(real_encoding)\n        except UnicodeDecodeError:\n            raise ValueError(\"data cannot be decoded with %s encoding\" % real_encoding)\n\n        return parse_qsl(\n            decoded_data,\n            keep_blank_values=True,\n            encoding=real_encoding,\n        )\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the boundary was reached or False otherwise.\"\"\"\n        return self._at_eof\n\n    def decode(self, data: bytes) -> bytes:\n        \"\"\"Decodes data.\n\n        Decoding is done according the specified Content-Encoding\n        or Content-Transfer-Encoding headers value.\n        \"\"\"\n        if CONTENT_TRANSFER_ENCODING in self.headers:\n            data = self._decode_content_transfer(data)\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n        if not self._is_form_data and CONTENT_ENCODING in self.headers:\n            return self._decode_content(data)\n        return data\n\n    def _decode_content(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_ENCODING, \"\").lower()\n        if encoding == \"identity\":\n            return data\n        if encoding in {\"deflate\", \"gzip\"}:\n            return ZLibDecompressor(\n                encoding=encoding,\n                suppress_deflate_header=True,\n            ).decompress_sync(data)\n\n        raise RuntimeError(f\"unknown content encoding: {encoding}\")\n\n    def _decode_content_transfer(self, data: bytes) -> bytes:\n        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n\n        if encoding == \"base64\":\n            return base64.b64decode(data)\n        elif encoding == \"quoted-printable\":\n            return binascii.a2b_qp(data)\n        elif encoding in (\"binary\", \"8bit\", \"7bit\"):\n            return data\n        else:\n            raise RuntimeError(\n                \"unknown content transfer encoding: {}\" \"\".format(encoding)\n            )\n\n    def get_charset(self, default: str) -> str:\n        \"\"\"Returns charset parameter from Content-Type header or default.\"\"\"\n        ctype = self.headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n        return mimetype.parameters.get(\"charset\", self._default_charset or default)\n\n    @reify\n    def name(self) -> Optional[str]:\n        \"\"\"Returns name specified in Content-Disposition header.\n\n        If the header is missing or malformed, returns None.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"name\")\n\n    @reify\n    def filename(self) -> Optional[str]:\n        \"\"\"Returns filename specified in Content-Disposition header.\n\n        Returns None if the header is missing or malformed.\n        \"\"\"\n        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))\n        return content_disposition_filename(params, \"filename\")\n\n\n@payload_type(BodyPartReader, order=Order.try_first)\nclass BodyPartReaderPayload(Payload):\n    def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:\n        super().__init__(value, *args, **kwargs)\n\n        params: Dict[str, str] = {}\n        if value.name is not None:\n            params[\"name\"] = value.name\n        if value.filename is not None:\n            params[\"filename\"] = value.filename\n\n        if params:\n            self.set_content_disposition(\"attachment\", True, **params)\n\n    async def write(self, writer: Any) -> None:\n        field = self._value\n        chunk = await field.read_chunk(size=2**16)\n        while chunk:\n            await writer.write(field.decode(chunk))\n            chunk = await field.read_chunk(size=2**16)\n\n\nclass MultipartReader:\n    \"\"\"Multipart body reader.\"\"\"\n\n    #: Response wrapper, used when multipart readers constructs from response.\n    response_wrapper_cls = MultipartResponseWrapper\n    #: Multipart reader class, used to handle multipart/* body parts.\n    #: None points to type(self)\n    multipart_reader_cls = None\n    #: Body part reader class for non multipart/* content types.\n    part_reader_cls = BodyPartReader\n\n    def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None:\n        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])\n        assert self._mimetype.type == \"multipart\", \"multipart/* content type expected\"\n        if \"boundary\" not in self._mimetype.parameters:\n            raise ValueError(\n                \"boundary missed for Content-Type: %s\" % headers[CONTENT_TYPE]\n            )\n\n        self.headers = headers\n        self._boundary = (\"--\" + self._get_boundary()).encode()\n        self._content = content\n        self._default_charset: Optional[str] = None\n        self._last_part: Optional[Union[\"MultipartReader\", BodyPartReader]] = None\n        self._at_eof = False\n        self._at_bof = True\n        self._unread: List[bytes] = []\n\n    def __aiter__(\n        self,\n    ) -> AsyncIterator[\"BodyPartReader\"]:\n        return self  # type: ignore[return-value]\n\n    async def __anext__(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        part = await self.next()\n        if part is None:\n            raise StopAsyncIteration\n        return part\n\n    @classmethod\n    def from_response(\n        cls,\n        response: \"ClientResponse\",\n    ) -> MultipartResponseWrapper:\n        \"\"\"Constructs reader instance from HTTP response.\n\n        :param response: :class:`~aiohttp.client.ClientResponse` instance\n        \"\"\"\n        obj = cls.response_wrapper_cls(\n            response, cls(response.headers, response.content)\n        )\n        return obj\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the final boundary was reached, false otherwise.\"\"\"\n        return self._at_eof\n\n    async def next(\n        self,\n    ) -> Optional[Union[\"MultipartReader\", BodyPartReader]]:\n        \"\"\"Emits the next multipart body part.\"\"\"\n        # So, if we're at BOF, we need to skip till the boundary.\n        if self._at_eof:\n            return None\n        await self._maybe_release_last_part()\n        if self._at_bof:\n            await self._read_until_first_boundary()\n            self._at_bof = False\n        else:\n            await self._read_boundary()\n        if self._at_eof:  # we just read the last boundary, nothing to do there\n            return None\n\n        part = await self.fetch_next_part()\n        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6\n        if (\n            self._last_part is None\n            and self._mimetype.subtype == \"form-data\"\n            and isinstance(part, BodyPartReader)\n        ):\n            _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION))\n            if params.get(\"name\") == \"_charset_\":\n                # Longest encoding in https://encoding.spec.whatwg.org/encodings.json\n                # is 19 characters, so 32 should be more than enough for any valid encoding.\n                charset = await part.read_chunk(32)\n                if len(charset) > 31:\n                    raise RuntimeError(\"Invalid default charset\")\n                self._default_charset = charset.strip().decode()\n                part = await self.fetch_next_part()\n        self._last_part = part\n        return self._last_part\n\n    async def release(self) -> None:\n        \"\"\"Reads all the body parts to the void till the final boundary.\"\"\"\n        while not self._at_eof:\n            item = await self.next()\n            if item is None:\n                break\n            await item.release()\n\n    async def fetch_next_part(\n        self,\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Returns the next body part reader.\"\"\"\n        headers = await self._read_headers()\n        return self._get_part_reader(headers)\n\n    def _get_part_reader(\n        self,\n        headers: \"CIMultiDictProxy[str]\",\n    ) -> Union[\"MultipartReader\", BodyPartReader]:\n        \"\"\"Dispatches the response by the `Content-Type` header.\n\n        Returns a suitable reader instance.\n\n        :param dict headers: Response headers\n        \"\"\"\n        ctype = headers.get(CONTENT_TYPE, \"\")\n        mimetype = parse_mimetype(ctype)\n\n        if mimetype.type == \"multipart\":\n            if self.multipart_reader_cls is None:\n                return type(self)(headers, self._content)\n            return self.multipart_reader_cls(headers, self._content)\n        else:\n            return self.part_reader_cls(\n                self._boundary,\n                headers,\n                self._content,\n                subtype=self._mimetype.subtype,\n                default_charset=self._default_charset,\n            )\n\n    def _get_boundary(self) -> str:\n        boundary = self._mimetype.parameters[\"boundary\"]\n        if len(boundary) > 70:\n            raise ValueError(\"boundary %r is too long (70 chars max)\" % boundary)\n\n        return boundary\n\n    async def _readline(self) -> bytes:\n        if self._unread:\n            return self._unread.pop()\n        return await self._content.readline()\n\n    async def _read_until_first_boundary(self) -> None:\n        while True:\n            chunk = await self._readline()\n            if chunk == b\"\":\n                raise ValueError(\n                    \"Could not find starting boundary %r\" % (self._boundary)\n                )\n            chunk = chunk.rstrip()\n            if chunk == self._boundary:\n                return\n            elif chunk == self._boundary + b\"--\":\n                self._at_eof = True\n                return\n\n    async def _read_boundary(self) -> None:\n        chunk = (await self._readline()).rstrip()\n        if chunk == self._boundary:\n            pass\n        elif chunk == self._boundary + b\"--\":\n            self._at_eof = True\n            epilogue = await self._readline()\n            next_line = await self._readline()\n\n            # the epilogue is expected and then either the end of input or the\n            # parent multipart boundary, if the parent boundary is found then\n            # it should be marked as unread and handed to the parent for\n            # processing\n            if next_line[:2] == b\"--\":\n                self._unread.append(next_line)\n            # otherwise the request is likely missing an epilogue and both\n            # lines should be passed to the parent for processing\n            # (this handles the old behavior gracefully)\n            else:\n                self._unread.extend([next_line, epilogue])\n        else:\n            raise ValueError(f\"Invalid boundary {chunk!r}, expected {self._boundary!r}\")\n\n    async def _read_headers(self) -> \"CIMultiDictProxy[str]\":\n        lines = [b\"\"]\n        while True:\n            chunk = await self._content.readline()\n            chunk = chunk.strip()\n            lines.append(chunk)\n            if not chunk:\n                break\n        parser = HeadersParser()\n        headers, raw_headers = parser.parse_headers(lines)\n        return headers\n\n    async def _maybe_release_last_part(self) -> None:\n        \"\"\"Ensures that the last read body part is read completely.\"\"\"\n        if self._last_part is not None:\n            if not self._last_part.at_eof():\n                await self._last_part.release()\n            self._unread.extend(self._last_part._unread)\n            self._last_part = None\n\n\n_Part = Tuple[Payload, str, str]\n\n\nclass MultipartWriter(Payload):\n    \"\"\"Multipart body writer.\"\"\"\n\n    def __init__(self, subtype: str = \"mixed\", boundary: Optional[str] = None) -> None:\n        boundary = boundary if boundary is not None else uuid.uuid4().hex\n        # The underlying Payload API demands a str (utf-8), not bytes,\n        # so we need to ensure we don't lose anything during conversion.\n        # As a result, require the boundary to be ASCII only.\n        # In both situations.\n\n        try:\n            self._boundary = boundary.encode(\"ascii\")\n        except UnicodeEncodeError:\n            raise ValueError(\"boundary should contain ASCII only chars\") from None\n        ctype = f\"multipart/{subtype}; boundary={self._boundary_value}\"\n\n        super().__init__(None, content_type=ctype)\n\n        self._parts: List[_Part] = []\n        self._is_form_data = subtype == \"form-data\"\n\n    def __enter__(self) -> \"MultipartWriter\":\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        pass\n\n    def __iter__(self) -> Iterator[_Part]:\n        return iter(self._parts)\n\n    def __len__(self) -> int:\n        return len(self._parts)\n\n    def __bool__(self) -> bool:\n        return True\n\n    _valid_tchar_regex = re.compile(rb\"\\A[!#$%&'*+\\-.^_`|~\\w]+\\Z\")\n    _invalid_qdtext_char_regex = re.compile(rb\"[\\x00-\\x08\\x0A-\\x1F\\x7F]\")\n\n    @property\n    def _boundary_value(self) -> str:\n        \"\"\"Wrap boundary parameter value in quotes, if necessary.\n\n        Reads self.boundary and returns a unicode string.\n        \"\"\"\n        # Refer to RFCs 7231, 7230, 5234.\n        #\n        # parameter      = token \"=\" ( token / quoted-string )\n        # token          = 1*tchar\n        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n        # obs-text       = %x80-FF\n        # quoted-pair    = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n        # tchar          = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n        #                  / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n        #                  / DIGIT / ALPHA\n        #                  ; any VCHAR, except delimiters\n        # VCHAR           = %x21-7E\n        value = self._boundary\n        if re.match(self._valid_tchar_regex, value):\n            return value.decode(\"ascii\")  # cannot fail\n\n        if re.search(self._invalid_qdtext_char_regex, value):\n            raise ValueError(\"boundary value contains invalid characters\")\n\n        # escape %x5C and %x22\n        quoted_value_content = value.replace(b\"\\\\\", b\"\\\\\\\\\")\n        quoted_value_content = quoted_value_content.replace(b'\"', b'\\\\\"')\n\n        return '\"' + quoted_value_content.decode(\"ascii\") + '\"'\n\n    @property\n    def boundary(self) -> str:\n        return self._boundary.decode(\"ascii\")\n\n    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Payload):\n            obj.headers.update(headers)\n            return self.append_payload(obj)\n        else:\n            try:\n                payload = get_payload(obj, headers=headers)\n            except LookupError:\n                raise TypeError(\"Cannot create payload from %r\" % obj)\n            else:\n                return self.append_payload(payload)\n\n    def append_payload(self, payload: Payload) -> Payload:\n        \"\"\"Adds a new body part to multipart writer.\"\"\"\n        encoding: Optional[str] = None\n        te_encoding: Optional[str] = None\n        if self._is_form_data:\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7\n            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8\n            assert CONTENT_DISPOSITION in payload.headers\n            assert \"name=\" in payload.headers[CONTENT_DISPOSITION]\n            assert (\n                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}\n                & payload.headers.keys()\n            )\n        else:\n            # compression\n            encoding = payload.headers.get(CONTENT_ENCODING, \"\").lower()\n            if encoding and encoding not in (\"deflate\", \"gzip\", \"identity\"):\n                raise RuntimeError(f\"unknown content encoding: {encoding}\")\n            if encoding == \"identity\":\n                encoding = None\n\n            # te encoding\n            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, \"\").lower()\n            if te_encoding not in (\"\", \"base64\", \"quoted-printable\", \"binary\"):\n                raise RuntimeError(f\"unknown content transfer encoding: {te_encoding}\")\n            if te_encoding == \"binary\":\n                te_encoding = None\n\n            # size\n            size = payload.size\n            if size is not None and not (encoding or te_encoding):\n                payload.headers[CONTENT_LENGTH] = str(size)\n\n        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]\n        return payload\n\n    def append_json(\n        self, obj: Any, headers: Optional[Mapping[str, str]] = None\n    ) -> Payload:\n        \"\"\"Helper to append JSON part.\"\"\"\n        if headers is None:\n            headers = CIMultiDict()\n\n        return self.append_payload(JsonPayload(obj, headers=headers))\n\n    def append_form(\n        self,\n        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],\n        headers: Optional[Mapping[str, str]] = None,\n    ) -> Payload:\n        \"\"\"Helper to append form urlencoded part.\"\"\"\n        assert isinstance(obj, (Sequence, Mapping))\n\n        if headers is None:\n            headers = CIMultiDict()\n\n        if isinstance(obj, Mapping):\n            obj = list(obj.items())\n        data = urlencode(obj, doseq=True)\n\n        return self.append_payload(\n            StringPayload(\n                data, headers=headers, content_type=\"application/x-www-form-urlencoded\"\n            )\n        )\n\n    @property\n    def size(self) -> Optional[int]:\n        \"\"\"Size of the payload.\"\"\"\n        total = 0\n        for part, encoding, te_encoding in self._parts:\n            if encoding or te_encoding or part.size is None:\n                return None\n\n            total += int(\n                2\n                + len(self._boundary)\n                + 2\n                + part.size  # b'--'+self._boundary+b'\\r\\n'\n                + len(part._binary_headers)\n                + 2  # b'\\r\\n'\n            )\n\n        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\\r\\n'\n        return total\n\n    async def write(self, writer: Any, close_boundary: bool = True) -> None:\n        \"\"\"Write body.\"\"\"\n        for part, encoding, te_encoding in self._parts:\n            await writer.write(b\"--\" + self._boundary + b\"\\r\\n\")\n            await writer.write(part._binary_headers)\n\n            if encoding or te_encoding:\n                w = MultipartPayloadWriter(writer)\n                if encoding:\n                    w.enable_compression(encoding)\n                if te_encoding:\n                    w.enable_encoding(te_encoding)\n                await part.write(w)  # type: ignore[arg-type]\n                await w.write_eof()\n            else:\n                await part.write(writer)\n\n            await writer.write(b\"\\r\\n\")\n\n        if close_boundary:\n            await writer.write(b\"--\" + self._boundary + b\"--\\r\\n\")\n\n\nclass MultipartPayloadWriter:\n    def __init__(self, writer: Any) -> None:\n        self._writer = writer\n        self._encoding: Optional[str] = None\n        self._compress: Optional[ZLibCompressor] = None\n        self._encoding_buffer: Optional[bytearray] = None\n\n    def enable_encoding(self, encoding: str) -> None:\n        if encoding == \"base64\":\n            self._encoding = encoding\n            self._encoding_buffer = bytearray()\n        elif encoding == \"quoted-printable\":\n            self._encoding = \"quoted-printable\"\n\n    def enable_compression(\n        self, encoding: str = \"deflate\", strategy: int = zlib.Z_DEFAULT_STRATEGY\n    ) -> None:\n        self._compress = ZLibCompressor(\n            encoding=encoding,\n            suppress_deflate_header=True,\n            strategy=strategy,\n        )\n\n    async def write_eof(self) -> None:\n        if self._compress is not None:\n            chunk = self._compress.flush()\n            if chunk:\n                self._compress = None\n                await self.write(chunk)\n\n        if self._encoding == \"base64\":\n            if self._encoding_buffer:\n                await self._writer.write(base64.b64encode(self._encoding_buffer))\n\n    async def write(self, chunk: bytes) -> None:\n        if self._compress is not None:\n            if chunk:\n                chunk = await self._compress.compress(chunk)\n                if not chunk:\n                    return\n\n        if self._encoding == \"base64\":\n            buf = self._encoding_buffer\n            assert buf is not None\n            buf.extend(chunk)\n\n            if buf:\n                div, mod = divmod(len(buf), 3)\n                enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :])\n                if enc_chunk:\n                    b64chunk = base64.b64encode(enc_chunk)\n                    await self._writer.write(b64chunk)\n        elif self._encoding == \"quoted-printable\":\n            await self._writer.write(binascii.b2a_qp(chunk))\n        else:\n            await self._writer.write(chunk)\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:** Content-Disposition Header Not Properly Set After Appending Payload in MultipartWriter

**Description:**
When using `MultipartWriter` to append a payload and set its `Content-Disposition`, the header does not get correctly set, causing related operations to fail.

**Example Buggy Code:**
```python
from aiohttp import MultipartWriter

writer = MultipartWriter("form-data")
payload = writer.append("some-data")
payload.set_content_disposition("form-data", name="method")
# At this point, Content-Disposition is expected to be set in payload.headers
```

In this scenario, after appending the payload and setting the `Content-Disposition`, the headers do not include the `Content-Disposition` field as expected.

**Expected Behavior:**
After setting the `Content-Disposition` on the payload, the `Content-Disposition` header should be present in the payload's headers with the specified parameters. This ensures that the multipart data is correctly formatted and can be processed as intended.

**Actual Behavior:**
The `Content-Disposition` header is missing from `payload.headers`, leading to failures in processes that rely on this header being correctly set.

[/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/multipart.py
import base64
import binascii
import json
import re
import uuid
import warnings
import zlib
from collections import deque
from types import TracebackType
from typing import (
    TYPE_CHECKING,
    Any,
    AsyncIterator,
    Deque,
    Dict,
    Iterator,
    List,
    Mapping,
    Optional,
    Sequence,
    Tuple,
    Type,
    Union,
    cast,
)
from urllib.parse import parse_qsl, unquote, urlencode

from multidict import CIMultiDict, CIMultiDictProxy

from .compression_utils import ZLibCompressor, ZLibDecompressor
from .hdrs import (
    CONTENT_DISPOSITION,
    CONTENT_ENCODING,
    CONTENT_LENGTH,
    CONTENT_TRANSFER_ENCODING,
    CONTENT_TYPE,
)
from .helpers import CHAR, TOKEN, parse_mimetype, reify
from .http import HeadersParser
from .payload import (
    JsonPayload,
    LookupError,
    Order,
    Payload,
    StringPayload,
    get_payload,
    payload_type,
)
from .streams import StreamReader

__all__ = (
    "MultipartReader",
    "MultipartWriter",
    "BodyPartReader",
    "BadContentDispositionHeader",
    "BadContentDispositionParam",
    "parse_content_disposition",
    "content_disposition_filename",
)


if TYPE_CHECKING:
    from .client_reqrep import ClientResponse


class BadContentDispositionHeader(RuntimeWarning):
    pass


class BadContentDispositionParam(RuntimeWarning):
    pass


def parse_content_disposition(
    header: Optional[str],
) -> Tuple[Optional[str], Dict[str, str]]:
    def is_token(string: str) -> bool:
        return bool(string) and TOKEN >= set(string)

    def is_quoted(string: str) -> bool:
        return string[0] == string[-1] == '"'

    def is_rfc5987(string: str) -> bool:
        return is_token(string) and string.count("'") == 2

    def is_extended_param(string: str) -> bool:
        return string.endswith("*")

    def is_continuous_param(string: str) -> bool:
        pos = string.find("*") + 1
        if not pos:
            return False
        substring = string[pos:-1] if string.endswith("*") else string[pos:]
        return substring.isdigit()

    def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str:
        return re.sub(f"\\\\([{chars}])", "\\1", text)

    if not header:
        return None, {}

    disptype, *parts = header.split(";")
    if not is_token(disptype):
        warnings.warn(BadContentDispositionHeader(header))
        return None, {}

    params: Dict[str, str] = {}
    while parts:
        item = parts.pop(0)

        if "=" not in item:
            warnings.warn(BadContentDispositionHeader(header))
            return None, {}

        key, value = item.split("=", 1)
        key = key.lower().strip()
        value = value.lstrip()

        if key in params:
            warnings.warn(BadContentDispositionHeader(header))
            return None, {}

        if not is_token(key):
            warnings.warn(BadContentDispositionParam(item))
            continue

        elif is_continuous_param(key):
            if is_quoted(value):
                value = unescape(value[1:-1])
            elif not is_token(value):
                warnings.warn(BadContentDispositionParam(item))
                continue

        elif is_extended_param(key):
            if is_rfc5987(value):
                encoding, _, value = value.split("'", 2)
                encoding = encoding or "utf-8"
            else:
                warnings.warn(BadContentDispositionParam(item))
                continue

            try:
                value = unquote(value, encoding, "strict")
            except UnicodeDecodeError:  # pragma: nocover
                warnings.warn(BadContentDispositionParam(item))
                continue

        else:
            failed = True
            if is_quoted(value):
                failed = False
                value = unescape(value[1:-1].lstrip("\\/"))
            elif is_token(value):
                failed = False
            elif parts:
                # maybe just ; in filename, in any case this is just
                # one case fix, for proper fix we need to redesign parser
                _value = f"{value};{parts[0]}"
                if is_quoted(_value):
                    parts.pop(0)
                    value = unescape(_value[1:-1].lstrip("\\/"))
                    failed = False

            if failed:
                warnings.warn(BadContentDispositionHeader(header))
                return None, {}

        params[key] = value

    return disptype.lower(), params


def content_disposition_filename(
    params: Mapping[str, str], name: str = "filename"
) -> Optional[str]:
    name_suf = "%s*" % name
    if not params:
        return None
    elif name_suf in params:
        return params[name_suf]
    elif name in params:
        return params[name]
    else:
        parts = []
        fnparams = sorted(
            (key, value) for key, value in params.items() if key.startswith(name_suf)
        )
        for num, (key, value) in enumerate(fnparams):
            _, tail = key.split("*", 1)
            if tail.endswith("*"):
                tail = tail[:-1]
            if tail == str(num):
                parts.append(value)
            else:
                break
        if not parts:
            return None
        value = "".join(parts)
        if "'" in value:
            encoding, _, value = value.split("'", 2)
            encoding = encoding or "utf-8"
            return unquote(value, encoding, "strict")
        return value


class MultipartResponseWrapper:
    """Wrapper around the MultipartReader.

    It takes care about
    underlying connection and close it when it needs in.
    """

    def __init__(
        self,
        resp: "ClientResponse",
        stream: "MultipartReader",
    ) -> None:
        self.resp = resp
        self.stream = stream

    def __aiter__(self) -> "MultipartResponseWrapper":
        return self

    async def __anext__(
        self,
    ) -> Union["MultipartReader", "BodyPartReader"]:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    def at_eof(self) -> bool:
        """Returns True when all response data had been read."""
        return self.resp.content.at_eof()

    async def next(
        self,
    ) -> Optional[Union["MultipartReader", "BodyPartReader"]]:
        """Emits next multipart reader object."""
        item = await self.stream.next()
        if self.stream.at_eof():
            await self.release()
        return item

    async def release(self) -> None:
        """Release the connection gracefully.

        All remaining content is read to the void.
        """
        await self.resp.release()


class BodyPartReader:
    """Multipart reader for single body part."""

    chunk_size = 8192

    def __init__(
        self,
        boundary: bytes,
        headers: "CIMultiDictProxy[str]",
        content: StreamReader,
        *,
        subtype: str = "mixed",
        default_charset: Optional[str] = None,
    ) -> None:
        self.headers = headers
        self._boundary = boundary
        self._content = content
        self._default_charset = default_charset
        self._at_eof = False
        self._is_form_data = subtype == "form-data"
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
        length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)
        self._length = int(length) if length is not None else None
        self._read_bytes = 0
        self._unread: Deque[bytes] = deque()
        self._prev_chunk: Optional[bytes] = None
        self._content_eof = 0
        self._cache: Dict[str, Any] = {}

    def __aiter__(self) -> AsyncIterator["BodyPartReader"]:
        return self  # type: ignore[return-value]

    async def __anext__(self) -> bytes:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    async def next(self) -> Optional[bytes]:
        item = await self.read()
        if not item:
            return None
        return item

    async def read(self, *, decode: bool = False) -> bytes:
        """Reads body part data.

        decode: Decodes data following by encoding
                method from Content-Encoding header. If it missed
                data remains untouched
        """
        if self._at_eof:
            return b""
        data = bytearray()
        while not self._at_eof:
            data.extend(await self.read_chunk(self.chunk_size))
        if decode:
            return self.decode(data)
        return data

    async def read_chunk(self, size: int = chunk_size) -> bytes:
        """Reads body part content chunk of the specified size.

        size: chunk size
        """
        if self._at_eof:
            return b""
        if self._length:
            chunk = await self._read_chunk_from_length(size)
        else:
            chunk = await self._read_chunk_from_stream(size)

        self._read_bytes += len(chunk)
        if self._read_bytes == self._length:
            self._at_eof = True
        if self._at_eof:
            clrf = await self._content.readline()
            assert (
                b"\r\n" == clrf
            ), "reader did not read all the data or it is malformed"
        return chunk

    async def _read_chunk_from_length(self, size: int) -> bytes:
        # Reads body part content chunk of the specified size.
        # The body part must has Content-Length header with proper value.
        assert self._length is not None, "Content-Length required for chunked read"
        chunk_size = min(size, self._length - self._read_bytes)
        chunk = await self._content.read(chunk_size)
        if self._content.at_eof():
            self._at_eof = True
        return chunk

    async def _read_chunk_from_stream(self, size: int) -> bytes:
        # Reads content chunk of body part with unknown length.
        # The Content-Length header for body part is not necessary.
        assert (
            size >= len(self._boundary) + 2
        ), "Chunk size must be greater or equal than boundary length + 2"
        first_chunk = self._prev_chunk is None
        if first_chunk:
            self._prev_chunk = await self._content.read(size)

        chunk = await self._content.read(size)
        self._content_eof += int(self._content.at_eof())
        assert self._content_eof < 3, "Reading after EOF"
        assert self._prev_chunk is not None
        window = self._prev_chunk + chunk
        sub = b"\r\n" + self._boundary
        if first_chunk:
            idx = window.find(sub)
        else:
            idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub)))
        if idx >= 0:
            # pushing boundary back to content
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=DeprecationWarning)
                self._content.unread_data(window[idx:])
            if size > idx:
                self._prev_chunk = self._prev_chunk[:idx]
            chunk = window[len(self._prev_chunk) : idx]
            if not chunk:
                self._at_eof = True
        result = self._prev_chunk
        self._prev_chunk = chunk
        return result

    async def readline(self) -> bytes:
        """Reads body part by line by line."""
        if self._at_eof:
            return b""

        if self._unread:
            line = self._unread.popleft()
        else:
            line = await self._content.readline()

        if line.startswith(self._boundary):
            # the very last boundary may not come with \r\n,
            # so set single rules for everyone
            sline = line.rstrip(b"\r\n")
            boundary = self._boundary
            last_boundary = self._boundary + b"--"
            # ensure that we read exactly the boundary, not something alike
            if sline == boundary or sline == last_boundary:
                self._at_eof = True
                self._unread.append(line)
                return b""
        else:
            next_line = await self._content.readline()
            if next_line.startswith(self._boundary):
                line = line[:-2]  # strip CRLF but only once
            self._unread.append(next_line)

        return line

    async def release(self) -> None:
        """Like read(), but reads all the data to the void."""
        if self._at_eof:
            return
        while not self._at_eof:
            await self.read_chunk(self.chunk_size)

    async def text(self, *, encoding: Optional[str] = None) -> str:
        """Like read(), but assumes that body part contains text data."""
        data = await self.read(decode=True)
        # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm
        # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send
        encoding = encoding or self.get_charset(default="utf-8")
        return data.decode(encoding)

    async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]:
        """Like read(), but assumes that body parts contains JSON data."""
        data = await self.read(decode=True)
        if not data:
            return None
        encoding = encoding or self.get_charset(default="utf-8")
        return cast(Dict[str, Any], json.loads(data.decode(encoding)))

    async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]:
        """Like read(), but assumes that body parts contain form urlencoded data."""
        data = await self.read(decode=True)
        if not data:
            return []
        if encoding is not None:
            real_encoding = encoding
        else:
            real_encoding = self.get_charset(default="utf-8")
        try:
            decoded_data = data.rstrip().decode(real_encoding)
        except UnicodeDecodeError:
            raise ValueError("data cannot be decoded with %s encoding" % real_encoding)

        return parse_qsl(
            decoded_data,
            keep_blank_values=True,
            encoding=real_encoding,
        )

    def at_eof(self) -> bool:
        """Returns True if the boundary was reached or False otherwise."""
        return self._at_eof

    def decode(self, data: bytes) -> bytes:
        """Decodes data.

        Decoding is done according the specified Content-Encoding
        or Content-Transfer-Encoding headers value.
        """
        if CONTENT_TRANSFER_ENCODING in self.headers:
            data = self._decode_content_transfer(data)
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
        if not self._is_form_data and CONTENT_ENCODING in self.headers:
            return self._decode_content(data)
        return data

    def _decode_content(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_ENCODING, "").lower()
        if encoding == "identity":
            return data
        if encoding in {"deflate", "gzip"}:
            return ZLibDecompressor(
                encoding=encoding,
                suppress_deflate_header=True,
            ).decompress_sync(data)

        raise RuntimeError(f"unknown content encoding: {encoding}")

    def _decode_content_transfer(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower()

        if encoding == "base64":
            return base64.b64decode(data)
        elif encoding == "quoted-printable":
            return binascii.a2b_qp(data)
        elif encoding in ("binary", "8bit", "7bit"):
            return data
        else:
            raise RuntimeError(
                "unknown content transfer encoding: {}" "".format(encoding)
            )

    def get_charset(self, default: str) -> str:
        """Returns charset parameter from Content-Type header or default."""
        ctype = self.headers.get(CONTENT_TYPE, "")
        mimetype = parse_mimetype(ctype)
        return mimetype.parameters.get("charset", self._default_charset or default)

    @reify
    def name(self) -> Optional[str]:
        """Returns name specified in Content-Disposition header.

        If the header is missing or malformed, returns None.
        """
        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
        return content_disposition_filename(params, "name")

    @reify
    def filename(self) -> Optional[str]:
        """Returns filename specified in Content-Disposition header.

        Returns None if the header is missing or malformed.
        """
        _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION))
        return content_disposition_filename(params, "filename")


@payload_type(BodyPartReader, order=Order.try_first)
class BodyPartReaderPayload(Payload):
    def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None:
        super().__init__(value, *args, **kwargs)

        params: Dict[str, str] = {}
        if value.name is not None:
            params["name"] = value.name
        if value.filename is not None:
            params["filename"] = value.filename

        if params:
            self.set_content_disposition("attachment", True, **params)

    async def write(self, writer: Any) -> None:
        field = self._value
        chunk = await field.read_chunk(size=2**16)
        while chunk:
            await writer.write(field.decode(chunk))
            chunk = await field.read_chunk(size=2**16)


class MultipartReader:
    """Multipart body reader."""

    #: Response wrapper, used when multipart readers constructs from response.
    response_wrapper_cls = MultipartResponseWrapper
    #: Multipart reader class, used to handle multipart/* body parts.
    #: None points to type(self)
    multipart_reader_cls = None
    #: Body part reader class for non multipart/* content types.
    part_reader_cls = BodyPartReader

    def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None:
        self._mimetype = parse_mimetype(headers[CONTENT_TYPE])
        assert self._mimetype.type == "multipart", "multipart/* content type expected"
        if "boundary" not in self._mimetype.parameters:
            raise ValueError(
                "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE]
            )

        self.headers = headers
        self._boundary = ("--" + self._get_boundary()).encode()
        self._content = content
        self._default_charset: Optional[str] = None
        self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None
        self._at_eof = False
        self._at_bof = True
        self._unread: List[bytes] = []

    def __aiter__(
        self,
    ) -> AsyncIterator["BodyPartReader"]:
        return self  # type: ignore[return-value]

    async def __anext__(
        self,
    ) -> Optional[Union["MultipartReader", BodyPartReader]]:
        part = await self.next()
        if part is None:
            raise StopAsyncIteration
        return part

    @classmethod
    def from_response(
        cls,
        response: "ClientResponse",
    ) -> MultipartResponseWrapper:
        """Constructs reader instance from HTTP response.

        :param response: :class:`~aiohttp.client.ClientResponse` instance
        """
        obj = cls.response_wrapper_cls(
            response, cls(response.headers, response.content)
        )
        return obj

    def at_eof(self) -> bool:
        """Returns True if the final boundary was reached, false otherwise."""
        return self._at_eof

    async def next(
        self,
    ) -> Optional[Union["MultipartReader", BodyPartReader]]:
        """Emits the next multipart body part."""
        # So, if we're at BOF, we need to skip till the boundary.
        if self._at_eof:
            return None
        await self._maybe_release_last_part()
        if self._at_bof:
            await self._read_until_first_boundary()
            self._at_bof = False
        else:
            await self._read_boundary()
        if self._at_eof:  # we just read the last boundary, nothing to do there
            return None

        part = await self.fetch_next_part()
        # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6
        if (
            self._last_part is None
            and self._mimetype.subtype == "form-data"
            and isinstance(part, BodyPartReader)
        ):
            _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION))
            if params.get("name") == "_charset_":
                # Longest encoding in https://encoding.spec.whatwg.org/encodings.json
                # is 19 characters, so 32 should be more than enough for any valid encoding.
                charset = await part.read_chunk(32)
                if len(charset) > 31:
                    raise RuntimeError("Invalid default charset")
                self._default_charset = charset.strip().decode()
                part = await self.fetch_next_part()
        self._last_part = part
        return self._last_part

    async def release(self) -> None:
        """Reads all the body parts to the void till the final boundary."""
        while not self._at_eof:
            item = await self.next()
            if item is None:
                break
            await item.release()

    async def fetch_next_part(
        self,
    ) -> Union["MultipartReader", BodyPartReader]:
        """Returns the next body part reader."""
        headers = await self._read_headers()
        return self._get_part_reader(headers)

    def _get_part_reader(
        self,
        headers: "CIMultiDictProxy[str]",
    ) -> Union["MultipartReader", BodyPartReader]:
        """Dispatches the response by the `Content-Type` header.

        Returns a suitable reader instance.

        :param dict headers: Response headers
        """
        ctype = headers.get(CONTENT_TYPE, "")
        mimetype = parse_mimetype(ctype)

        if mimetype.type == "multipart":
            if self.multipart_reader_cls is None:
                return type(self)(headers, self._content)
            return self.multipart_reader_cls(headers, self._content)
        else:
            return self.part_reader_cls(
                self._boundary,
                headers,
                self._content,
                subtype=self._mimetype.subtype,
                default_charset=self._default_charset,
            )

    def _get_boundary(self) -> str:
        boundary = self._mimetype.parameters["boundary"]
        if len(boundary) > 70:
            raise ValueError("boundary %r is too long (70 chars max)" % boundary)

        return boundary

    async def _readline(self) -> bytes:
        if self._unread:
            return self._unread.pop()
        return await self._content.readline()

    async def _read_until_first_boundary(self) -> None:
        while True:
            chunk = await self._readline()
            if chunk == b"":
                raise ValueError(
                    "Could not find starting boundary %r" % (self._boundary)
                )
            chunk = chunk.rstrip()
            if chunk == self._boundary:
                return
            elif chunk == self._boundary + b"--":
                self._at_eof = True
                return

    async def _read_boundary(self) -> None:
        chunk = (await self._readline()).rstrip()
        if chunk == self._boundary:
            pass
        elif chunk == self._boundary + b"--":
            self._at_eof = True
            epilogue = await self._readline()
            next_line = await self._readline()

            # the epilogue is expected and then either the end of input or the
            # parent multipart boundary, if the parent boundary is found then
            # it should be marked as unread and handed to the parent for
            # processing
            if next_line[:2] == b"--":
                self._unread.append(next_line)
            # otherwise the request is likely missing an epilogue and both
            # lines should be passed to the parent for processing
            # (this handles the old behavior gracefully)
            else:
                self._unread.extend([next_line, epilogue])
        else:
            raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}")

    async def _read_headers(self) -> "CIMultiDictProxy[str]":
        lines = [b""]
        while True:
            chunk = await self._content.readline()
            chunk = chunk.strip()
            lines.append(chunk)
            if not chunk:
                break
        parser = HeadersParser()
        headers, raw_headers = parser.parse_headers(lines)
        return headers

    async def _maybe_release_last_part(self) -> None:
        """Ensures that the last read body part is read completely."""
        if self._last_part is not None:
            if not self._last_part.at_eof():
                await self._last_part.release()
            self._unread.extend(self._last_part._unread)
            self._last_part = None


_Part = Tuple[Payload, str, str]


class MultipartWriter(Payload):
    """Multipart body writer."""

    def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None:
        boundary = boundary if boundary is not None else uuid.uuid4().hex
        # The underlying Payload API demands a str (utf-8), not bytes,
        # so we need to ensure we don't lose anything during conversion.
        # As a result, require the boundary to be ASCII only.
        # In both situations.

        try:
            self._boundary = boundary.encode("ascii")
        except UnicodeEncodeError:
            raise ValueError("boundary should contain ASCII only chars") from None
        ctype = f"multipart/{subtype}; boundary={self._boundary_value}"

        super().__init__(None, content_type=ctype)

        self._parts: List[_Part] = []
        self._is_form_data = subtype == "form-data"

    def __enter__(self) -> "MultipartWriter":
        return self

    def __exit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> None:
        pass

    def __iter__(self) -> Iterator[_Part]:
        return iter(self._parts)

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

    def __bool__(self) -> bool:
        return True

    _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z")
    _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]")

    @property
    def _boundary_value(self) -> str:
        """Wrap boundary parameter value in quotes, if necessary.

        Reads self.boundary and returns a unicode string.
        """
        # Refer to RFCs 7231, 7230, 5234.
        #
        # parameter      = token "=" ( token / quoted-string )
        # token          = 1*tchar
        # quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
        # qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
        # obs-text       = %x80-FF
        # quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )
        # tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
        #                  / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
        #                  / DIGIT / ALPHA
        #                  ; any VCHAR, except delimiters
        # VCHAR           = %x21-7E
        value = self._boundary
        if re.match(self._valid_tchar_regex, value):
            return value.decode("ascii")  # cannot fail

        if re.search(self._invalid_qdtext_char_regex, value):
            raise ValueError("boundary value contains invalid characters")

        # escape %x5C and %x22
        quoted_value_content = value.replace(b"\\", b"\\\\")
        quoted_value_content = quoted_value_content.replace(b'"', b'\\"')

        return '"' + quoted_value_content.decode("ascii") + '"'

    @property
    def boundary(self) -> str:
        return self._boundary.decode("ascii")

    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:
        if headers is None:
            headers = CIMultiDict()

        if isinstance(obj, Payload):
            obj.headers.update(headers)
            return self.append_payload(obj)
        else:
            try:
                payload = get_payload(obj, headers=headers)
            except LookupError:
                raise TypeError("Cannot create payload from %r" % obj)
            else:
                return self.append_payload(payload)

    def append_payload(self, payload: Payload) -> Payload:
        """Adds a new body part to multipart writer."""
        encoding: Optional[str] = None
        te_encoding: Optional[str] = None
        if self._is_form_data:
            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7
            # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
            assert CONTENT_DISPOSITION in payload.headers
            assert "name=" in payload.headers[CONTENT_DISPOSITION]
            assert (
                not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING}
                & payload.headers.keys()
            )
        else:
            # compression
            encoding = payload.headers.get(CONTENT_ENCODING, "").lower()
            if encoding and encoding not in ("deflate", "gzip", "identity"):
                raise RuntimeError(f"unknown content encoding: {encoding}")
            if encoding == "identity":
                encoding = None

            # te encoding
            te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, "").lower()
            if te_encoding not in ("", "base64", "quoted-printable", "binary"):
                raise RuntimeError(f"unknown content transfer encoding: {te_encoding}")
            if te_encoding == "binary":
                te_encoding = None

            # size
            size = payload.size
            if size is not None and not (encoding or te_encoding):
                payload.headers[CONTENT_LENGTH] = str(size)

        self._parts.append((payload, encoding, te_encoding))  # type: ignore[arg-type]
        return payload

    def append_json(
        self, obj: Any, headers: Optional[Mapping[str, str]] = None
    ) -> Payload:
        """Helper to append JSON part."""
        if headers is None:
            headers = CIMultiDict()

        return self.append_payload(JsonPayload(obj, headers=headers))

    def append_form(
        self,
        obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],
        headers: Optional[Mapping[str, str]] = None,
    ) -> Payload:
        """Helper to append form urlencoded part."""
        assert isinstance(obj, (Sequence, Mapping))

        if headers is None:
            headers = CIMultiDict()

        if isinstance(obj, Mapping):
            obj = list(obj.items())
        data = urlencode(obj, doseq=True)

        return self.append_payload(
            StringPayload(
                data, headers=headers, content_type="application/x-www-form-urlencoded"
            )
        )

    @property
    def size(self) -> Optional[int]:
        """Size of the payload."""
        total = 0
        for part, encoding, te_encoding in self._parts:
            if encoding or te_encoding or part.size is None:
                return None

            total += int(
                2
                + len(self._boundary)
                + 2
                + part.size  # b'--'+self._boundary+b'\r\n'
                + len(part._binary_headers)
                + 2  # b'\r\n'
            )

        total += 2 + len(self._boundary) + 4  # b'--'+self._boundary+b'--\r\n'
        return total

    async def write(self, writer: Any, close_boundary: bool = True) -> None:
        """Write body."""
        for part, encoding, te_encoding in self._parts:
            await writer.write(b"--" + self._boundary + b"\r\n")
            await writer.write(part._binary_headers)

            if encoding or te_encoding:
                w = MultipartPayloadWriter(writer)
                if encoding:
                    w.enable_compression(encoding)
                if te_encoding:
                    w.enable_encoding(te_encoding)
                await part.write(w)  # type: ignore[arg-type]
                await w.write_eof()
            else:
                await part.write(writer)

            await writer.write(b"\r\n")

        if close_boundary:
            await writer.write(b"--" + self._boundary + b"--\r\n")


class MultipartPayloadWriter:
    def __init__(self, writer: Any) -> None:
        self._writer = writer
        self._encoding: Optional[str] = None
        self._compress: Optional[ZLibCompressor] = None
        self._encoding_buffer: Optional[bytearray] = None

    def enable_encoding(self, encoding: str) -> None:
        if encoding == "base64":
            self._encoding = encoding
            self._encoding_buffer = bytearray()
        elif encoding == "quoted-printable":
            self._encoding = "quoted-printable"

    def enable_compression(
        self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY
    ) -> None:
        self._compress = ZLibCompressor(
            encoding=encoding,
            suppress_deflate_header=True,
            strategy=strategy,
        )

    async def write_eof(self) -> None:
        if self._compress is not None:
            chunk = self._compress.flush()
            if chunk:
                self._compress = None
                await self.write(chunk)

        if self._encoding == "base64":
            if self._encoding_buffer:
                await self._writer.write(base64.b64encode(self._encoding_buffer))

    async def write(self, chunk: bytes) -> None:
        if self._compress is not None:
            if chunk:
                chunk = await self._compress.compress(chunk)
                if not chunk:
                    return

        if self._encoding == "base64":
            buf = self._encoding_buffer
            assert buf is not None
            buf.extend(chunk)

            if buf:
                div, mod = divmod(len(buf), 3)
                enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :])
                if enc_chunk:
                    b64chunk = base64.b64encode(enc_chunk)
                    await self._writer.write(b64chunk)
        elif self._encoding == "quoted-printable":
            await self._writer.write(binascii.b2a_qp(chunk))
        else:
            await self._writer.write(chunk)

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