You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support async file types in files = {} and content = ... #1620
We probably want to ensure that we're supporting both trio, anyio (Which have the same interfaces), and perhaps also `aiofiles. So eg, also supporting the following...
# Supporting the same as above but using `asyncio`, with `anyio` for the file operations.importanyioimportasyncioimporthttpxasyncdefmain():
asyncwithhttpx.AsyncClient() asclient:
asyncwithawaitanyio.open_file(...) asf:
client.post("https://www.example.com", content=f)
asyncio.run(main())
The content=... case is a little simpler than the data=... case, since it really just need an async variant of peek_filelike_length, and a minor update to the ._content.encode_content() function.
Also fiddly is what the type annotations ought to look like.
Reacted by Yevhenii Kovalchuk, Matt Webster, Eduard Iskandarov, Mayst, Braden Pellett, Timothée Mazzucotelli, Hannes Ljungberg, T-256, My-Responsitories, MatPag and 8 more
Hi, I'm interested in this issue and also found it in the ._content.encode_content() function, the first thing need to do is to find the type of trio and anyio. Could I have a try for getting it done? :D
hey, @meist0731 are you still working on this issue ? If yes, can we work it together. Seems like an interesting problem
Yep! I'm still working on this. It's my honor to work together with you :DDD I'm going to sleep soon and tomorrow I will share the materials I've searched before.
async def main1():
async with await anyio.open_file('./content.txt','rb') as f:
await client.post("https://www.example.com", content=f)
anyio.run(main1)
async def main2():
async with await trio.Path('./content.txt').open('rb') as f:
await client.post("https://www.example.com", content=f)
trio.run(main2)
async def main3():
async with aiofiles.open('./content.txt', mode='rb') as f:
await client.post("https://www.example.com", content=f)
asyncio.run(main3())
The multipart upload:
async def main5():
async with httpx.AsyncClient() as client:
async with await anyio.open_file('./content.txt','rb') as f:
await client.post("https://www.example.com", files={"upload": f})
anyio.run(main5)
The problems here are:
(1) The above functions only support read files in "rb" mode instead of 'r', otherwise it will give a TypeError saying "sequence item 1: expected a bytes-like object, str found", but I haven't figure out which part of code is handling this.
(2) I've tested the above functions with a text file, it proved that no matter reading a file in a sync way or async way using trio, anyio or aiofiles, the .peek_filelike_length function can get the file's length correctly. However, when it comes to multipart upload, the error shows "AsyncIOWrapper"/"AsyncFile"/"AsyncBufferedReader" (Asynciterable object) is not iterable. It seems because the iteration functions here are all sync functions instead of async functions cannot support iterate an Asynciterable object but still receive Asynciterable objects, except for the final one is an async function
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Just want to make sure: uploading a file and data as multipart is still not supported by the async client, right? I'm getting the Attempted to send an sync request with an AsyncClient instance. error message when trying to do such a thing.
Let me help guide this conversation a bit more clearly.
I would probably suggest starting by just looking at the content=... case.
A good starting point for a pull request would be a test case for that one case, which demonstrates the behaviour we'd like to see.
What's the status of this ticket? There were a couple people actively working toward a PR several years ago, and it's unclear what happened with that work. Since then, conversation has revolved entirely around monkeypatches, temp fixes, and workarounds.
Is anyone actively looking at the proper solution within httpx itself?
I am also in need of using aiofiles async streams with multipart uploading.
OK thanks, since that comment was a year ago I guess the answer is that the status of this ticket is low priority and will not be done by the maintainer.
async def __aiter__(self) -> t.AsyncIterator[bytes]:
for field in self.fields:
yield b'--%s\r\n' % self.boundary
if isinstance(field, FileField) and isinstance(field.file, AsyncStreamWrapper):
yield field.render_headers()
async for chunk in field.file.stream:
yield chunk
else:
for chunk in field.render():
yield chunk
yield b'\r\n'
yield b'--%s--\r\n' % self.boundary
In case if someone is using this too. Be warned that async for chunk in field.file.stream is really slow (see same bug inside httpx was fixed here #1948)
Activity
Mayst0731 commentedon Jun 3, 2021
Hi, I'm interested in this issue and also found it in the ._content.encode_content() function, the first thing need to do is to find the type of trio and anyio. Could I have a try for getting it done? :D
Mayst0731 commentedon Jun 3, 2021
Ohhh, like you said its multipart issue seems like aiohttp working with multipart as well.
("https://docs.aiohttp.org/en/stable/multipart.html")
ajayd-san commentedon Jun 11, 2021
hey, @meist0731 are you still working on this issue ? If yes, can we work it together. Seems like an interesting problem
Mayst0731 commentedon Jun 11, 2021
Yep! I'm still working on this. It's my honor to work together with you :DDD I'm going to sleep soon and tomorrow I will share the materials I've searched before.
ajayd-san commentedon Jun 11, 2021
@meist0731 cheers, you on discord ? itll be easier to work together.
Mayst0731 commentedon Jun 11, 2021
Gotcha! I have discord, wait a sec, bro.
Mayst0731 commentedon Jun 11, 2021
Hey, I've sent the invitation :D
ajayd-san commentedon Jun 21, 2021
@tomchristie how do you recommend to proceed this issue? Like can you explain where to start and all ?
Mayst0731 commentedon Jun 24, 2021
I've tried these APIs as below,
The multipart upload:
The problems here are:
(1) The above functions only support read files in "rb" mode instead of 'r', otherwise it will give a TypeError saying "sequence item 1: expected a bytes-like object, str found", but I haven't figure out which part of code is handling this.
(2) I've tested the above functions with a text file, it proved that no matter reading a file in a sync way or async way using trio, anyio or aiofiles, the .peek_filelike_length function can get the file's length correctly. However, when it comes to multipart upload, the error shows "AsyncIOWrapper"/"AsyncFile"/"AsyncBufferedReader" (Asynciterable object) is not iterable. It seems because the iteration functions here are all sync functions instead of async functions cannot support iterate an Asynciterable object but still receive Asynciterable objects, except for the final one is an async function
For example, this function accepts AsyncIterable objects but cannot perform iterations indeed. is not an async function.
Mayst0731 commentedon Jun 24, 2021
The first problem has an existing discussion #1704 (comment)
stale commentedon Feb 20, 2022
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
pawamoy commentedon Jun 16, 2022
Argh, stale, wontfix, nooo 😱 !
Just want to make sure: uploading a file and data as multipart is still not supported by the async client, right? I'm getting the
Attempted to send an sync request with an AsyncClient instance.error message when trying to do such a thing.2 remaining items
reclosedev commentedon Dec 12, 2022
While the issue is not resolved, I'm using following monkey-patch, maybe it will be helpful:
and3rson commentedon Apr 16, 2023
Has there been any progress on this by any chance?
lambdaq commentedon May 19, 2023
I am also using
files={"upload": f}wherefis a multi-part async file upload from FastAPI.It says
TypeError: object of type 'coroutine' has no len()which kills me. The file is quite large I hope it gets handled in a streamable way.lovelydinosaur commentedon May 19, 2023
If anyone is invested in making this happen I can make the time to guide a pull request through.
lambdaq commentedon Jun 7, 2023
I solved this problem, for FastAPI. When reading a uploaded file in a form, FastAPI wraps a SpooledTemporaryFile into async style.
To access the file with httpx, the async doesn't fit, but you can use the old-fasioned way, just change
httpx.post(..., files={"upload": f})into
httpx.post(..., files={"upload": f.file})yayahuman commentedon Jul 6, 2023
A monkey patch showing a possible solution (also cancels #1706 and covers #2399):
https://github.com/proxy/gist.github.com/yayahuman/db06718ffdf8a9b66e133e29d7d7965f
And possible type annotations:
yayahuman commentedon Jul 10, 2023
@tomchristie, can my monkey patch approach be acceptable?
lovelydinosaur commentedon Jul 11, 2023
Let me help guide this conversation a bit more clearly.
I would probably suggest starting by just looking at the
content=...case.A good starting point for a pull request would be a test case for that one case, which demonstrates the behaviour we'd like to see.
dkbarn commentedon Jul 1, 2024
What's the status of this ticket? There were a couple people actively working toward a PR several years ago, and it's unclear what happened with that work. Since then, conversation has revolved entirely around monkeypatches, temp fixes, and workarounds.
Is anyone actively looking at the proper solution within httpx itself?
I am also in need of using
aiofilesasync streams with multipart uploading.lovelydinosaur commentedon Jul 2, 2024
#1620 (comment)
dkbarn commentedon Jul 2, 2024
OK thanks, since that comment was a year ago I guess the answer is that the status of this ticket is low priority and will not be done by the maintainer.
Mayst0731 commentedon Jul 2, 2024
i see, it would be better if giving more context/details about your train of thoughts of this content case? thanks~~ @tomchristie
lovelydinosaur commentedon Jul 2, 2024
I'd suggest a good approach would be that I reciprocate effort by guiding contributors through the process.
xbeastx commentedon Apr 12, 2025
@reclosedev Thanks for this solution.
In case if someone is using this too. Be warned that
async for chunk in field.file.streamis really slow (see same bug inside httpx was fixed here #1948)So efficient will be: