@@ -392,14 +392,18 @@ def _get_js_body(body):
392392 "Response" ,
393393}
394394
395+ # JS built-in types that should NOT be wrapped in _BindingWrapper.
396+ # These have their own Python-side semantics (e.g. passed directly to Response())
397+ # and wrapping them breaks property access like `.constructor.name`.
398+ _JS_PASSTHROUGH_TYPES = RESPONSE_ACCEPTED_TYPES | {
399+ "Headers" ,
400+ }
395401
396- def _is_response_accepted_type (obj ) -> bool :
397- """
398- Check if the given object is an accepted type for a Response body.
399- """
402+
403+ def _get_js_constructor_name (obj ) -> str | None :
400404 if hasattr (obj , "constructor" ):
401- return obj .constructor .name in RESPONSE_ACCEPTED_TYPES
402- return False
405+ return obj .constructor .name
406+ return None
403407
404408
405409class Response (FetchResponse ):
@@ -423,11 +427,9 @@ def __init__(
423427 https://developer.mozilla.org/en-US/docs/Web/API/Response/Response.
424428 """
425429 # Verify passed in types.
426- if hasattr (body , "constructor" ):
427- if not _is_response_accepted_type (body ):
428- raise TypeError (
429- f"Unsupported type in Response: { body .constructor .name } "
430- )
430+ js_type = _get_js_constructor_name (body )
431+ if js_type not in RESPONSE_ACCEPTED_TYPES :
432+ raise TypeError (f"Unsupported type in Response: { js_type } " )
431433 elif not isinstance (body , str | FormData | bytes ) and body is not None :
432434 raise TypeError (f"Unsupported type in Response: { type (body ).__name__ } " )
433435
@@ -1123,18 +1125,18 @@ def _should_wrap_nested_attribute(self, jsobj) -> bool:
11231125 if not isinstance (jsobj , JsProxy ):
11241126 return False
11251127
1126- return not _is_response_accepted_type (jsobj )
1128+ # TODO: This allowlist approach is a workaround. The long-term fix is to
1129+ # add dedicated Python wrappers for these types in python_from_rpc so they
1130+ # never reach _BindingWrapper in the first place.
1131+ js_type = _get_js_constructor_name (jsobj )
1132+ return js_type not in _JS_PASSTHROUGH_TYPES
11271133
11281134 def _convert_result (self , result ):
11291135 converted = python_from_rpc (result )
11301136
11311137 # After python_from_rpc, some objects may still be JsProxy objects.
1132- # For now, we wrap all of them except the ones that are already accepted as responses
1133- # with the _BindingWrapper (or a subclass of it)
1134- # so that accessing attributes on them will be properly converted.
1135-
1136- # TODO: This is a bit of a hack. We should revisit when there are more
1137- # bindings to support with different return types.
1138+ # We need to wrap them with _BindingWrapper (or a subclass of it) again
1139+ # to ensure that accessing attributes on them will be properly converted.
11381140 if self ._should_wrap_nested_attribute (converted ):
11391141 return self .__class__ (converted )
11401142 if isinstance (converted , list ):
0 commit comments