Skip to content

Commit 9bb6f6c

Browse files
committed
Adds support for bytes passed to Python's Response constructor
1 parent a14eccb commit 9bb6f6c

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

src/pyodide/internal/workers-api/src/workers/_workers.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,23 @@ def _to_js_headers(headers: Headers):
314314
raise TypeError("Received unexpected type for headers argument")
315315

316316

317+
@contextmanager
318+
def _get_js_body(body):
319+
if isinstance(body, bytes):
320+
proxy_bytes = create_proxy(body)
321+
proxy_buffer = proxy_bytes.getBuffer()
322+
try:
323+
yield proxy_buffer.data
324+
return
325+
finally:
326+
proxy_buffer.release()
327+
proxy_bytes.destroy()
328+
if isinstance(body, FormData):
329+
yield body.js_object
330+
return
331+
yield body
332+
333+
317334
class Response(FetchResponse):
318335
"""
319336
This class represents the response to an HTTP request, with a similar API to that of the web
@@ -349,7 +366,7 @@ def __init__(
349366
raise TypeError(
350367
f"Unsupported type in Response: {body.constructor.name}"
351368
)
352-
elif not isinstance(body, str | FormData) and body is not None:
369+
elif not isinstance(body, str | FormData | bytes) and body is not None:
353370
raise TypeError(f"Unsupported type in Response: {type(body).__name__}")
354371

355372
# Handle constructing a Response from a JS Response.
@@ -363,11 +380,11 @@ def __init__(
363380

364381
options = self._create_options(status, status_text, headers, web_socket)
365382

366-
# Initialize via the FetchResponse super-class which gives us access to
367-
# methods that we would ordinarily have to redeclare.
368-
js_resp = js.Response.new(
369-
body.js_object if isinstance(body, FormData) else body, **options
370-
)
383+
# To avoid unnecessary copies we use this context manager.
384+
with _get_js_body(body) as js_body:
385+
# Initialize via the FetchResponse super-class which gives us access to
386+
# methods that we would ordinarily have to redeclare.
387+
js_resp = js.Response.new(js_body, **options)
371388
super().__init__(js_resp.url, js_resp)
372389

373390
def __repr__(self):

src/workerd/server/tests/python/sdk/worker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,10 @@ async def response_unit_tests(env):
476476
assert response_none.status == 204
477477
assert response_none.body is None
478478

479+
response_bytes = Response(b"test")
480+
assert response_bytes.status == 200
481+
assert await response_bytes.text() == "test"
482+
479483
class Test:
480484
def __init__(self, x):
481485
self.x = x

0 commit comments

Comments
 (0)