Repository: aio-libs/aiohttp

Issue:
Invalid type definition for mpwriter.append
### Describe the bug

It would appear that the `append` method of the MultiPartWriter has an incorrect type definition:

https://github.com/aio-libs/aiohttp/blob/d8936aacf7d602a58228d95fd469bbf01a43216d/aiohttp/multipart.py#L856

Resulting in type checking tools to incorrectly errpr what (I believe) should be correct.

### To Reproduce

The following test file generates the error in type checkers.

```py
import aiohttp

with aiohttp.MultipartWriter() as mpwriter:
    mpwriter.append("Hello, World", {"Content-Type": "text/plain"})
```

which generates the following errors:

```console
❯ mypy test.py
test.py:4: error: Argument 2 to "append" of "MultipartWriter" has incompatible type "dict[str, str]"; expected "MultiMapping[str] | None"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

❯ pyright test.py
/Users/joshua.ellis/src/pact-foundation/pact-python/tests/v3/test.py
  /Users/joshua.ellis/src/pact-foundation/pact-python/tests/v3/test.py:4:37 - error: Argument of type "dict[str, str]" cannot be assigned to parameter "headers" of type "MultiMapping[str] | None" in function "append"
    Type "dict[str, str]" cannot be assigned to type "MultiMapping[str] | None"
      "dict[str, str]" is incompatible with "MultiMapping[str]"
      Type cannot be assigned to type "None" (reportGeneralTypeIssues)
1 error, 0 warnings, 0 informations
```

### Expected behavior

I expected that a regular Python dictionary be compatible with the header argument.

### Logs/tracebacks

```python-traceback
_see above_
```


### Python Version

```console
❯ python --version
Python 3.11.6
```


### aiohttp Version

```console
❯ python -m pip show aiohttp
Name: aiohttp
Version: 3.8.6
Summary: Async http client/server framework (asyncio)
Home-page: https://github.com/aio-libs/aiohttp
Author: 
Author-email: 
License: Apache 2
Requires: aiosignal, async-timeout, attrs, charset-normalizer, frozenlist, multidict, yarl
Required-by:
```


### multidict Version

```console
❯ python -m pip show multidict
Name: multidict
Version: 6.0.4
Summary: multidict implementation
Home-page: https://github.com/aio-libs/multidict
Author: Andrew Svetlov
Author-email: andrew.svetlov@gmail.com
License: Apache 2
Requires: 
Required-by: aiohttp, yarl
```


### yarl Version

```console
❯ python -m pip show yarl
Name: yarl
Version: 1.9.2
Summary: Yet another URL library
Home-page: https://github.com/aio-libs/yarl/
Author: Andrew Svetlov
Author-email: andrew.svetlov@gmail.com
License: Apache-2.0
Requires: idna, multidict
Required-by: aiohttp
```


### OS

```console
❯ uname -mprs
Darwin 22.6.0 arm64 arm
```

### Related component

Client

### Additional context

_No response_

### Code of Conduct

- [X] I agree to follow the aio-libs Code of Conduct


Discussion:
Yes, it appears the argument is always passed into `CIMultiDict.update()`, which should accept a `Mapping`. Feel free to make a PR to change the annotation (I suspect you'll need to tweak a couple of other annotations too).
Great! I'll work on it later this week :)

Resolving patch:
diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py
--- a/aiohttp/multipart.py
+++ b/aiohttp/multipart.py
@@ -25,7 +25,7 @@
 )
 from urllib.parse import parse_qsl, unquote, urlencode
 
-from multidict import CIMultiDict, CIMultiDictProxy, MultiMapping
+from multidict import CIMultiDict, CIMultiDictProxy
 
 from .compression_utils import ZLibCompressor, ZLibDecompressor
 from .hdrs import (
@@ -853,7 +853,7 @@ def _boundary_value(self) -> str:
     def boundary(self) -> str:
         return self._boundary.decode("ascii")
 
-    def append(self, obj: Any, headers: Optional[MultiMapping[str]] = None) -> Payload:
+    def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload:
         if headers is None:
             headers = CIMultiDict()
 
@@ -901,7 +901,7 @@ def append_payload(self, payload: Payload) -> Payload:
         return payload
 
     def append_json(
-        self, obj: Any, headers: Optional[MultiMapping[str]] = None
+        self, obj: Any, headers: Optional[Mapping[str, str]] = None
     ) -> Payload:
         """Helper to append JSON part."""
         if headers is None:
@@ -912,7 +912,7 @@ def append_json(
     def append_form(
         self,
         obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]],
-        headers: Optional[MultiMapping[str]] = None,
+        headers: Optional[Mapping[str, str]] = None,
     ) -> Payload:
         """Helper to append form urlencoded part."""
         assert isinstance(obj, (Sequence, Mapping))


Test patch: