55from contextlib import ExitStack , contextmanager
66from enum import StrEnum
77from http import HTTPMethod , HTTPStatus
8- from typing import TypedDict , Unpack
8+ from typing import Any , TypedDict , Unpack
99
1010import js
1111
2121Headers = dict [str , str ] | list [tuple [str , str ]]
2222
2323
24+ # https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties
25+ class RequestInitCfProperties (TypedDict , total = False ):
26+ apps : bool | None
27+ cacheEverything : bool | None
28+ cacheKey : str | None
29+ cacheTags : list [str ] | None
30+ cacheTtl : int
31+ cacheTtlByStatus : dict [str , int ]
32+ image : (
33+ Any | None
34+ ) # TODO: https://developers.cloudflare.com/images/transform-images/transform-via-workers/
35+ mirage : bool | None
36+ polish : str | None
37+ resolveOverride : str | None
38+ scrapeShield : bool | None
39+ webp : bool | None
40+
41+
42+ # This matches the Request options:
43+ # https://developers.cloudflare.com/workers/runtime-apis/request/#options
2444class FetchKwargs (TypedDict , total = False ):
2545 headers : Headers | None
2646 body : "Body | None"
2747 method : HTTPMethod = HTTPMethod .GET
48+ redirect : str | None
49+ cf : RequestInitCfProperties | None
2850
2951
3052# TODO: Pyodide's FetchResponse.headers returns a dict[str, str] which means
@@ -65,6 +87,15 @@ async def formData(self) -> "FormData":
6587 except JsException as exc :
6688 raise _to_python_exception (exc ) from exc
6789
90+ def replace_body (self , body : Body ) -> "FetchResponse" :
91+ """
92+ Returns a new Response object with the same options (status, headers, etc) as
93+ the original but with an updated body.
94+ """
95+ b = body .js_object if isinstance (body , FormData ) else body
96+ js_resp = js .Response .new (b , self .js_response )
97+ return FetchResponse (js_resp .url , js_resp )
98+
6899
69100async def fetch (
70101 resource : str ,
@@ -99,7 +130,7 @@ def __init__(
99130 self ,
100131 body : Body ,
101132 status : HTTPStatus | int = HTTPStatus .OK ,
102- statusText = "" ,
133+ status_text = "" ,
103134 headers : Headers = None ,
104135 ):
105136 """
@@ -108,7 +139,7 @@ def __init__(
108139 Based on the JS API of the same name:
109140 https://developer.mozilla.org/en-US/docs/Web/API/Response/Response.
110141 """
111- options = self ._create_options (status , statusText , headers )
142+ options = self ._create_options (status , status_text , headers )
112143
113144 # Initialise via the FetchResponse super-class which gives us access to
114145 # methods that we would ordinarily have to redeclare.
@@ -119,13 +150,15 @@ def __init__(
119150
120151 @staticmethod
121152 def _create_options (
122- status : HTTPStatus | int = HTTPStatus .OK , statusText = "" , headers : Headers = None
153+ status : HTTPStatus | int = HTTPStatus .OK ,
154+ status_text = "" ,
155+ headers : Headers = None ,
123156 ):
124157 options = {
125158 "status" : status .value if isinstance (status , HTTPStatus ) else status ,
126159 }
127- if len ( statusText ) > 0 :
128- options ["statusText" ] = statusText
160+ if status_text :
161+ options ["statusText" ] = status_text
129162 if headers :
130163 if isinstance (headers , list ):
131164 # We should have a list[tuple[str, str]]
@@ -154,10 +187,10 @@ def redirect(url: str, status: HTTPStatus | int = HTTPStatus.FOUND):
154187 def json (
155188 data : str | dict [str , str ],
156189 status : HTTPStatus | int = HTTPStatus .OK ,
157- statusText = "" ,
190+ status_text = "" ,
158191 headers : Headers = None ,
159192 ):
160- options = Response ._create_options (status , statusText , headers )
193+ options = Response ._create_options (status , status_text , headers )
161194 with _manage_pyproxies () as pyproxies :
162195 try :
163196 return js .Response .json (
0 commit comments