We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AsyncClient.request runtime error: "Attempted to send an sync request with an AsyncClient instance"
Ran into this issue as well. It was surprising to end up with a `RuntimeError: Attempted to send an sync request with an AsyncClient instance` error, when my actual issue had nothing to do with sync/async (in my case submitting the wrong type by accident through `data` kwarg). It feels like it might be a bug / that the type being submitted could be checked before trying to send the request?

### Discussed in https://github.com/encode/httpx/discussions/2339

<div type='discussions-op-text'>

<sup>Originally posted by **mdlincoln** August 16, 2022</sup>
I need to interact with an API that accepts `DELETE` requests requiring JSON content in the body. Since `AsyncClient`'s shortcut `delete()` method does not allow this, I followed the docs to instead use the `request()` method, but I received an unexpected error. Minimal example:

``` py
import asyncio
from httpx import AsyncClient

async_client = AsyncClient(
    base_url="http://localhost",
)

async def test_request_method():
    async with async_client as ac:
        response = await ac.request(method="DELETE", url="/", content={"foo": "bar"})

asyncio.run(test_request_method())
```

this raises a `RuntimeError: Attempted to send an sync request with an AsyncClient instance.` (full traceback in the `<details>` below.)

Am I missing the right way for calling the `request()` method with an `AsyncClient`?

<details>
<pre>
Traceback (most recent call last):
  File "*******/minimal.py", line 14, in <module>
    asyncio.run(test_request_method())
  File "/opt/homebrew/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/opt/homebrew/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "*********minimal.py", line 11, in test_request_method
    response = await ac.request(method="DELETE", url="/", content={"foo": "bar"})
  File "*********/lib/python3.9/site-packages/httpx/_client.py", line 1527, in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "*********/lib/python3.9/site-packages/httpx/_client.py", line 1614, in send
    response = await self._send_handling_auth(
  File "*********/lib/python3.9/site-packages/httpx/_client.py", line 1642, in _send_handling_auth
    response = await self._send_handling_redirects(
  File "*********/lib/python3.9/site-packages/httpx/_client.py", line 1679, in _send_handling_redirects
    response = await self._send_single_request(request)
  File "*********/lib/python3.9/site-packages/httpx/_client.py", line 1711, in _send_single_request
    raise RuntimeError(
RuntimeError: Attempted to send an sync request with an AsyncClient instance.
</pre>
</details>
</div>

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `httpx/_content.py`
Content:
```
1 import inspect
2 import warnings
3 from json import dumps as json_dumps
4 from typing import (
5     Any,
6     AsyncIterable,
7     AsyncIterator,
8     Dict,
9     Iterable,
10     Iterator,
11     Mapping,
12     Optional,
13     Tuple,
14     Union,
15 )
16 from urllib.parse import urlencode
17 
18 from ._exceptions import StreamClosed, StreamConsumed
19 from ._multipart import MultipartStream
20 from ._types import (
21     AsyncByteStream,
22     RequestContent,
23     RequestData,
24     RequestFiles,
25     ResponseContent,
26     SyncByteStream,
27 )
28 from ._utils import peek_filelike_length, primitive_value_to_str
29 
30 
31 class ByteStream(AsyncByteStream, SyncByteStream):
32     def __init__(self, stream: bytes) -> None:
33         self._stream = stream
34 
35     def __iter__(self) -> Iterator[bytes]:
36         yield self._stream
37 
38     async def __aiter__(self) -> AsyncIterator[bytes]:
39         yield self._stream
40 
41 
42 class IteratorByteStream(SyncByteStream):
43     CHUNK_SIZE = 65_536
44 
45     def __init__(self, stream: Iterable[bytes]):
46         self._stream = stream
47         self._is_stream_consumed = False
48         self._is_generator = inspect.isgenerator(stream)
49 
50     def __iter__(self) -> Iterator[bytes]:
51         if self._is_stream_consumed and self._is_generator:
52             raise StreamConsumed()
53 
54         self._is_stream_consumed = True
55         if hasattr(self._stream, "read"):
56             # File-like interfaces should use 'read' directly.
57             chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore
58             while chunk:
59                 yield chunk
60                 chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore
61         else:
62             # Otherwise iterate.
63             for part in self._stream:
64                 yield part
65 
66 
67 class AsyncIteratorByteStream(AsyncByteStream):
68     CHUNK_SIZE = 65_536
69 
70     def __init__(self, stream: AsyncIterable[bytes]):
71         self._stream = stream
72         self._is_stream_consumed = False
73         self._is_generator = inspect.isasyncgen(stream)
74 
75     async def __aiter__(self) -> AsyncIterator[bytes]:
76         if self._is_stream_consumed and self._is_generator:
77             raise StreamConsumed()
78 
79         self._is_stream_consumed = True
80         if hasattr(self._stream, "aread"):
81             # File-like interfaces should use 'aread' directly.
82             chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore
83             while chunk:
84                 yield chunk
85                 chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore
86         else:
87             # Otherwise iterate.
88             async for part in self._stream:
89                 yield part
90 
91 
92 class UnattachedStream(AsyncByteStream, SyncByteStream):
93     """
94     If a request or response is serialized using pickle, then it is no longer
95     attached to a stream for I/O purposes. Any stream operations should result
96     in `httpx.StreamClosed`.
97     """
98 
99     def __iter__(self) -> Iterator[bytes]:
100         raise StreamClosed()
101 
102     async def __aiter__(self) -> AsyncIterator[bytes]:
103         raise StreamClosed()
104         yield b""  # pragma: no cover
105 
106 
107 def encode_content(
108     content: Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]
109 ) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:
110 
111     if isinstance(content, (bytes, str)):
112         body = content.encode("utf-8") if isinstance(content, str) else content
113         content_length = len(body)
114         headers = {"Content-Length": str(content_length)} if body else {}
115         return headers, ByteStream(body)
116 
117     elif isinstance(content, Iterable):
118         content_length_or_none = peek_filelike_length(content)
119 
120         if content_length_or_none is None:
121             headers = {"Transfer-Encoding": "chunked"}
122         else:
123             headers = {"Content-Length": str(content_length_or_none)}
124         return headers, IteratorByteStream(content)  # type: ignore
125 
126     elif isinstance(content, AsyncIterable):
127         headers = {"Transfer-Encoding": "chunked"}
128         return headers, AsyncIteratorByteStream(content)
129 
130     raise TypeError(f"Unexpected type for 'content', {type(content)!r}")
131 
132 
133 def encode_urlencoded_data(
134     data: RequestData,
135 ) -> Tuple[Dict[str, str], ByteStream]:
136     plain_data = []
137     for key, value in data.items():
138         if isinstance(value, (list, tuple)):
139             plain_data.extend([(key, primitive_value_to_str(item)) for item in value])
140         else:
141             plain_data.append((key, primitive_value_to_str(value)))
142     body = urlencode(plain_data, doseq=True).encode("utf-8")
143     content_length = str(len(body))
144     content_type = "application/x-www-form-urlencoded"
145     headers = {"Content-Length": content_length, "Content-Type": content_type}
146     return headers, ByteStream(body)
147 
148 
149 def encode_multipart_data(
150     data: RequestData, files: RequestFiles, boundary: Optional[bytes]
151 ) -> Tuple[Dict[str, str], MultipartStream]:
152     multipart = MultipartStream(data=data, files=files, boundary=boundary)
153     headers = multipart.get_headers()
154     return headers, multipart
155 
156 
157 def encode_text(text: str) -> Tuple[Dict[str, str], ByteStream]:
158     body = text.encode("utf-8")
159     content_length = str(len(body))
160     content_type = "text/plain; charset=utf-8"
161     headers = {"Content-Length": content_length, "Content-Type": content_type}
162     return headers, ByteStream(body)
163 
164 
165 def encode_html(html: str) -> Tuple[Dict[str, str], ByteStream]:
166     body = html.encode("utf-8")
167     content_length = str(len(body))
168     content_type = "text/html; charset=utf-8"
169     headers = {"Content-Length": content_length, "Content-Type": content_type}
170     return headers, ByteStream(body)
171 
172 
173 def encode_json(json: Any) -> Tuple[Dict[str, str], ByteStream]:
174     body = json_dumps(json).encode("utf-8")
175     content_length = str(len(body))
176     content_type = "application/json"
177     headers = {"Content-Length": content_length, "Content-Type": content_type}
178     return headers, ByteStream(body)
179 
180 
181 def encode_request(
182     content: Optional[RequestContent] = None,
183     data: Optional[RequestData] = None,
184     files: Optional[RequestFiles] = None,
185     json: Optional[Any] = None,
186     boundary: Optional[bytes] = None,
187 ) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:
188     """
189     Handles encoding the given `content`, `data`, `files`, and `json`,
190     returning a two-tuple of (<headers>, <stream>).
191     """
192     if data is not None and not isinstance(data, Mapping):
193         # We prefer to separate `content=<bytes|str|byte iterator|bytes aiterator>`
194         # for raw request content, and `data=<form data>` for url encoded or
195         # multipart form content.
196         #
197         # However for compat with requests, we *do* still support
198         # `data=<bytes...>` usages. We deal with that case here, treating it
199         # as if `content=<...>` had been supplied instead.
200         message = "Use 'content=<...>' to upload raw bytes/text content."
201         warnings.warn(message, DeprecationWarning)
202         return encode_content(data)
203 
204     if content is not None:
205         return encode_content(content)
206     elif files:
207         return encode_multipart_data(data or {}, files, boundary)
208     elif data:
209         return encode_urlencoded_data(data)
210     elif json is not None:
211         return encode_json(json)
212 
213     return {}, ByteStream(b"")
214 
215 
216 def encode_response(
217     content: Optional[ResponseContent] = None,
218     text: Optional[str] = None,
219     html: Optional[str] = None,
220     json: Optional[Any] = None,
221 ) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:
222     """
223     Handles encoding the given `content`, returning a two-tuple of
224     (<headers>, <stream>).
225     """
226     if content is not None:
227         return encode_content(content)
228     elif text is not None:
229         return encode_text(text)
230     elif html is not None:
231         return encode_html(html)
232     elif json is not None:
233         return encode_json(json)
234 
235     return {}, ByteStream(b"")
236
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/httpx/_content.py b/httpx/_content.py
--- a/httpx/_content.py
+++ b/httpx/_content.py
@@ -114,7 +114,11 @@
         headers = {"Content-Length": str(content_length)} if body else {}
         return headers, ByteStream(body)
 
-    elif isinstance(content, Iterable):
+    elif isinstance(content, Iterable) and not isinstance(content, dict):
+        # `not isinstance(content, dict)` is a bit oddly specific, but it
+        # catches a case that's easy for users to make in error, and would
+        # otherwise pass through here, like any other bytes-iterable,
+        # because `dict` happens to be iterable. See issue #2491.
         content_length_or_none = peek_filelike_length(content)
 
         if content_length_or_none is None:

{"golden_diff": "diff --git a/httpx/_content.py b/httpx/_content.py\n--- a/httpx/_content.py\n+++ b/httpx/_content.py\n@@ -114,7 +114,11 @@\n         headers = {\"Content-Length\": str(content_length)} if body else {}\n         return headers, ByteStream(body)\n \n-    elif isinstance(content, Iterable):\n+    elif isinstance(content, Iterable) and not isinstance(content, dict):\n+        # `not isinstance(content, dict)` is a bit oddly specific, but it\n+        # catches a case that's easy for users to make in error, and would\n+        # otherwise pass through here, like any other bytes-iterable,\n+        # because `dict` happens to be iterable. See issue #2491.\n         content_length_or_none = peek_filelike_length(content)\n \n         if content_length_or_none is None:\n", "issue": "AsyncClient.request runtime error: \"Attempted to send an sync request with an AsyncClient instance\"\nRan into this issue as well. It was surprising to end up with a `RuntimeError: Attempted to send an sync request with an AsyncClient instance` error, when my actual issue had nothing to do with sync/async (in my case submitting the wrong type by accident through `data` kwarg). It feels like it might be a bug / that the type being submitted could be checked before trying to send the request?\r\n\r\n### Discussed in https://github.com/encode/httpx/discussions/2339\r\n\r\n<div type='discussions-op-text'>\r\n\r\n<sup>Originally posted by **mdlincoln** August 16, 2022</sup>\r\nI need to interact with an API that accepts `DELETE` requests requiring JSON content in the body. Since `AsyncClient`'s shortcut `delete()` method does not allow this, I followed the docs to instead use the `request()` method, but I received an unexpected error. Minimal example:\r\n\r\n``` py\r\nimport asyncio\r\nfrom httpx import AsyncClient\r\n\r\nasync_client = AsyncClient(\r\n    base_url=\"http://localhost\",\r\n)\r\n\r\nasync def test_request_method():\r\n    async with async_client as ac:\r\n        response = await ac.request(method=\"DELETE\", url=\"/\", content={\"foo\": \"bar\"})\r\n\r\nasyncio.run(test_request_method())\r\n```\r\n\r\nthis raises a `RuntimeError: Attempted to send an sync request with an AsyncClient instance.` (full traceback in the `<details>` below.)\r\n\r\nAm I missing the right way for calling the `request()` method with an `AsyncClient`?\r\n\r\n<details>\r\n<pre>\r\nTraceback (most recent call last):\r\n  File \"*******/minimal.py\", line 14, in <module>\r\n    asyncio.run(test_request_method())\r\n  File \"/opt/homebrew/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py\", line 44, in run\r\n    return loop.run_until_complete(main)\r\n  File \"/opt/homebrew/Cellar/python@3.9/3.9.13_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py\", line 647, in run_until_complete\r\n    return future.result()\r\n  File \"*********minimal.py\", line 11, in test_request_method\r\n    response = await ac.request(method=\"DELETE\", url=\"/\", content={\"foo\": \"bar\"})\r\n  File \"*********/lib/python3.9/site-packages/httpx/_client.py\", line 1527, in request\r\n    return await self.send(request, auth=auth, follow_redirects=follow_redirects)\r\n  File \"*********/lib/python3.9/site-packages/httpx/_client.py\", line 1614, in send\r\n    response = await self._send_handling_auth(\r\n  File \"*********/lib/python3.9/site-packages/httpx/_client.py\", line 1642, in _send_handling_auth\r\n    response = await self._send_handling_redirects(\r\n  File \"*********/lib/python3.9/site-packages/httpx/_client.py\", line 1679, in _send_handling_redirects\r\n    response = await self._send_single_request(request)\r\n  File \"*********/lib/python3.9/site-packages/httpx/_client.py\", line 1711, in _send_single_request\r\n    raise RuntimeError(\r\nRuntimeError: Attempted to send an sync request with an AsyncClient instance.\r\n</pre>\r\n</details>\r\n</div>\n", "before_files": [{"content": "import inspect\nimport warnings\nfrom json import dumps as json_dumps\nfrom typing import (\n    Any,\n    AsyncIterable,\n    AsyncIterator,\n    Dict,\n    Iterable,\n    Iterator,\n    Mapping,\n    Optional,\n    Tuple,\n    Union,\n)\nfrom urllib.parse import urlencode\n\nfrom ._exceptions import StreamClosed, StreamConsumed\nfrom ._multipart import MultipartStream\nfrom ._types import (\n    AsyncByteStream,\n    RequestContent,\n    RequestData,\n    RequestFiles,\n    ResponseContent,\n    SyncByteStream,\n)\nfrom ._utils import peek_filelike_length, primitive_value_to_str\n\n\nclass ByteStream(AsyncByteStream, SyncByteStream):\n    def __init__(self, stream: bytes) -> None:\n        self._stream = stream\n\n    def __iter__(self) -> Iterator[bytes]:\n        yield self._stream\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        yield self._stream\n\n\nclass IteratorByteStream(SyncByteStream):\n    CHUNK_SIZE = 65_536\n\n    def __init__(self, stream: Iterable[bytes]):\n        self._stream = stream\n        self._is_stream_consumed = False\n        self._is_generator = inspect.isgenerator(stream)\n\n    def __iter__(self) -> Iterator[bytes]:\n        if self._is_stream_consumed and self._is_generator:\n            raise StreamConsumed()\n\n        self._is_stream_consumed = True\n        if hasattr(self._stream, \"read\"):\n            # File-like interfaces should use 'read' directly.\n            chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore\n            while chunk:\n                yield chunk\n                chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore\n        else:\n            # Otherwise iterate.\n            for part in self._stream:\n                yield part\n\n\nclass AsyncIteratorByteStream(AsyncByteStream):\n    CHUNK_SIZE = 65_536\n\n    def __init__(self, stream: AsyncIterable[bytes]):\n        self._stream = stream\n        self._is_stream_consumed = False\n        self._is_generator = inspect.isasyncgen(stream)\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        if self._is_stream_consumed and self._is_generator:\n            raise StreamConsumed()\n\n        self._is_stream_consumed = True\n        if hasattr(self._stream, \"aread\"):\n            # File-like interfaces should use 'aread' directly.\n            chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore\n            while chunk:\n                yield chunk\n                chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore\n        else:\n            # Otherwise iterate.\n            async for part in self._stream:\n                yield part\n\n\nclass UnattachedStream(AsyncByteStream, SyncByteStream):\n    \"\"\"\n    If a request or response is serialized using pickle, then it is no longer\n    attached to a stream for I/O purposes. Any stream operations should result\n    in `httpx.StreamClosed`.\n    \"\"\"\n\n    def __iter__(self) -> Iterator[bytes]:\n        raise StreamClosed()\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        raise StreamClosed()\n        yield b\"\"  # pragma: no cover\n\n\ndef encode_content(\n    content: Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n\n    if isinstance(content, (bytes, str)):\n        body = content.encode(\"utf-8\") if isinstance(content, str) else content\n        content_length = len(body)\n        headers = {\"Content-Length\": str(content_length)} if body else {}\n        return headers, ByteStream(body)\n\n    elif isinstance(content, Iterable):\n        content_length_or_none = peek_filelike_length(content)\n\n        if content_length_or_none is None:\n            headers = {\"Transfer-Encoding\": \"chunked\"}\n        else:\n            headers = {\"Content-Length\": str(content_length_or_none)}\n        return headers, IteratorByteStream(content)  # type: ignore\n\n    elif isinstance(content, AsyncIterable):\n        headers = {\"Transfer-Encoding\": \"chunked\"}\n        return headers, AsyncIteratorByteStream(content)\n\n    raise TypeError(f\"Unexpected type for 'content', {type(content)!r}\")\n\n\ndef encode_urlencoded_data(\n    data: RequestData,\n) -> Tuple[Dict[str, str], ByteStream]:\n    plain_data = []\n    for key, value in data.items():\n        if isinstance(value, (list, tuple)):\n            plain_data.extend([(key, primitive_value_to_str(item)) for item in value])\n        else:\n            plain_data.append((key, primitive_value_to_str(value)))\n    body = urlencode(plain_data, doseq=True).encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"application/x-www-form-urlencoded\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_multipart_data(\n    data: RequestData, files: RequestFiles, boundary: Optional[bytes]\n) -> Tuple[Dict[str, str], MultipartStream]:\n    multipart = MultipartStream(data=data, files=files, boundary=boundary)\n    headers = multipart.get_headers()\n    return headers, multipart\n\n\ndef encode_text(text: str) -> Tuple[Dict[str, str], ByteStream]:\n    body = text.encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"text/plain; charset=utf-8\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_html(html: str) -> Tuple[Dict[str, str], ByteStream]:\n    body = html.encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"text/html; charset=utf-8\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_json(json: Any) -> Tuple[Dict[str, str], ByteStream]:\n    body = json_dumps(json).encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"application/json\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_request(\n    content: Optional[RequestContent] = None,\n    data: Optional[RequestData] = None,\n    files: Optional[RequestFiles] = None,\n    json: Optional[Any] = None,\n    boundary: Optional[bytes] = None,\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n    \"\"\"\n    Handles encoding the given `content`, `data`, `files`, and `json`,\n    returning a two-tuple of (<headers>, <stream>).\n    \"\"\"\n    if data is not None and not isinstance(data, Mapping):\n        # We prefer to separate `content=<bytes|str|byte iterator|bytes aiterator>`\n        # for raw request content, and `data=<form data>` for url encoded or\n        # multipart form content.\n        #\n        # However for compat with requests, we *do* still support\n        # `data=<bytes...>` usages. We deal with that case here, treating it\n        # as if `content=<...>` had been supplied instead.\n        message = \"Use 'content=<...>' to upload raw bytes/text content.\"\n        warnings.warn(message, DeprecationWarning)\n        return encode_content(data)\n\n    if content is not None:\n        return encode_content(content)\n    elif files:\n        return encode_multipart_data(data or {}, files, boundary)\n    elif data:\n        return encode_urlencoded_data(data)\n    elif json is not None:\n        return encode_json(json)\n\n    return {}, ByteStream(b\"\")\n\n\ndef encode_response(\n    content: Optional[ResponseContent] = None,\n    text: Optional[str] = None,\n    html: Optional[str] = None,\n    json: Optional[Any] = None,\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n    \"\"\"\n    Handles encoding the given `content`, returning a two-tuple of\n    (<headers>, <stream>).\n    \"\"\"\n    if content is not None:\n        return encode_content(content)\n    elif text is not None:\n        return encode_text(text)\n    elif html is not None:\n        return encode_html(html)\n    elif json is not None:\n        return encode_json(json)\n\n    return {}, ByteStream(b\"\")\n", "path": "httpx/_content.py"}], "after_files": [{"content": "import inspect\nimport warnings\nfrom json import dumps as json_dumps\nfrom typing import (\n    Any,\n    AsyncIterable,\n    AsyncIterator,\n    Dict,\n    Iterable,\n    Iterator,\n    Mapping,\n    Optional,\n    Tuple,\n    Union,\n)\nfrom urllib.parse import urlencode\n\nfrom ._exceptions import StreamClosed, StreamConsumed\nfrom ._multipart import MultipartStream\nfrom ._types import (\n    AsyncByteStream,\n    RequestContent,\n    RequestData,\n    RequestFiles,\n    ResponseContent,\n    SyncByteStream,\n)\nfrom ._utils import peek_filelike_length, primitive_value_to_str\n\n\nclass ByteStream(AsyncByteStream, SyncByteStream):\n    def __init__(self, stream: bytes) -> None:\n        self._stream = stream\n\n    def __iter__(self) -> Iterator[bytes]:\n        yield self._stream\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        yield self._stream\n\n\nclass IteratorByteStream(SyncByteStream):\n    CHUNK_SIZE = 65_536\n\n    def __init__(self, stream: Iterable[bytes]):\n        self._stream = stream\n        self._is_stream_consumed = False\n        self._is_generator = inspect.isgenerator(stream)\n\n    def __iter__(self) -> Iterator[bytes]:\n        if self._is_stream_consumed and self._is_generator:\n            raise StreamConsumed()\n\n        self._is_stream_consumed = True\n        if hasattr(self._stream, \"read\"):\n            # File-like interfaces should use 'read' directly.\n            chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore\n            while chunk:\n                yield chunk\n                chunk = self._stream.read(self.CHUNK_SIZE)  # type: ignore\n        else:\n            # Otherwise iterate.\n            for part in self._stream:\n                yield part\n\n\nclass AsyncIteratorByteStream(AsyncByteStream):\n    CHUNK_SIZE = 65_536\n\n    def __init__(self, stream: AsyncIterable[bytes]):\n        self._stream = stream\n        self._is_stream_consumed = False\n        self._is_generator = inspect.isasyncgen(stream)\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        if self._is_stream_consumed and self._is_generator:\n            raise StreamConsumed()\n\n        self._is_stream_consumed = True\n        if hasattr(self._stream, \"aread\"):\n            # File-like interfaces should use 'aread' directly.\n            chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore\n            while chunk:\n                yield chunk\n                chunk = await self._stream.aread(self.CHUNK_SIZE)  # type: ignore\n        else:\n            # Otherwise iterate.\n            async for part in self._stream:\n                yield part\n\n\nclass UnattachedStream(AsyncByteStream, SyncByteStream):\n    \"\"\"\n    If a request or response is serialized using pickle, then it is no longer\n    attached to a stream for I/O purposes. Any stream operations should result\n    in `httpx.StreamClosed`.\n    \"\"\"\n\n    def __iter__(self) -> Iterator[bytes]:\n        raise StreamClosed()\n\n    async def __aiter__(self) -> AsyncIterator[bytes]:\n        raise StreamClosed()\n        yield b\"\"  # pragma: no cover\n\n\ndef encode_content(\n    content: Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n\n    if isinstance(content, (bytes, str)):\n        body = content.encode(\"utf-8\") if isinstance(content, str) else content\n        content_length = len(body)\n        headers = {\"Content-Length\": str(content_length)} if body else {}\n        return headers, ByteStream(body)\n\n    elif isinstance(content, Iterable) and not isinstance(content, dict):\n        # `not isinstance(content, dict)` is a bit oddly specific, but it\n        # catches a case that's easy for users to make in error, and would\n        # otherwise pass through here, like any other bytes-iterable,\n        # because `dict` happens to be iterable. See issue #2491.\n        content_length_or_none = peek_filelike_length(content)\n\n        if content_length_or_none is None:\n            headers = {\"Transfer-Encoding\": \"chunked\"}\n        else:\n            headers = {\"Content-Length\": str(content_length_or_none)}\n        return headers, IteratorByteStream(content)  # type: ignore\n\n    elif isinstance(content, AsyncIterable):\n        headers = {\"Transfer-Encoding\": \"chunked\"}\n        return headers, AsyncIteratorByteStream(content)\n\n    raise TypeError(f\"Unexpected type for 'content', {type(content)!r}\")\n\n\ndef encode_urlencoded_data(\n    data: RequestData,\n) -> Tuple[Dict[str, str], ByteStream]:\n    plain_data = []\n    for key, value in data.items():\n        if isinstance(value, (list, tuple)):\n            plain_data.extend([(key, primitive_value_to_str(item)) for item in value])\n        else:\n            plain_data.append((key, primitive_value_to_str(value)))\n    body = urlencode(plain_data, doseq=True).encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"application/x-www-form-urlencoded\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_multipart_data(\n    data: RequestData, files: RequestFiles, boundary: Optional[bytes]\n) -> Tuple[Dict[str, str], MultipartStream]:\n    multipart = MultipartStream(data=data, files=files, boundary=boundary)\n    headers = multipart.get_headers()\n    return headers, multipart\n\n\ndef encode_text(text: str) -> Tuple[Dict[str, str], ByteStream]:\n    body = text.encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"text/plain; charset=utf-8\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_html(html: str) -> Tuple[Dict[str, str], ByteStream]:\n    body = html.encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"text/html; charset=utf-8\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_json(json: Any) -> Tuple[Dict[str, str], ByteStream]:\n    body = json_dumps(json).encode(\"utf-8\")\n    content_length = str(len(body))\n    content_type = \"application/json\"\n    headers = {\"Content-Length\": content_length, \"Content-Type\": content_type}\n    return headers, ByteStream(body)\n\n\ndef encode_request(\n    content: Optional[RequestContent] = None,\n    data: Optional[RequestData] = None,\n    files: Optional[RequestFiles] = None,\n    json: Optional[Any] = None,\n    boundary: Optional[bytes] = None,\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n    \"\"\"\n    Handles encoding the given `content`, `data`, `files`, and `json`,\n    returning a two-tuple of (<headers>, <stream>).\n    \"\"\"\n    if data is not None and not isinstance(data, Mapping):\n        # We prefer to separate `content=<bytes|str|byte iterator|bytes aiterator>`\n        # for raw request content, and `data=<form data>` for url encoded or\n        # multipart form content.\n        #\n        # However for compat with requests, we *do* still support\n        # `data=<bytes...>` usages. We deal with that case here, treating it\n        # as if `content=<...>` had been supplied instead.\n        message = \"Use 'content=<...>' to upload raw bytes/text content.\"\n        warnings.warn(message, DeprecationWarning)\n        return encode_content(data)\n\n    if content is not None:\n        return encode_content(content)\n    elif files:\n        return encode_multipart_data(data or {}, files, boundary)\n    elif data:\n        return encode_urlencoded_data(data)\n    elif json is not None:\n        return encode_json(json)\n\n    return {}, ByteStream(b\"\")\n\n\ndef encode_response(\n    content: Optional[ResponseContent] = None,\n    text: Optional[str] = None,\n    html: Optional[str] = None,\n    json: Optional[Any] = None,\n) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:\n    \"\"\"\n    Handles encoding the given `content`, returning a two-tuple of\n    (<headers>, <stream>).\n    \"\"\"\n    if content is not None:\n        return encode_content(content)\n    elif text is not None:\n        return encode_text(text)\n    elif html is not None:\n        return encode_html(html)\n    elif json is not None:\n        return encode_json(json)\n\n    return {}, ByteStream(b\"\")\n", "path": "httpx/_content.py"}]}