mirror of https://github.com/pyodide/pyodide.git
Don't throw when calling str on a proxy without a toString method (#4574)
Resolves #4569. It still doesn't make sure `str(jsproxy)` never throws. It will throw if: 1. accessing `obj.toString` succeeds and returns a function, but calling the function throws 2. accessing `obj.toString` fails or returns not a function, and `Object.prototype.toString.call` fails.
This commit is contained in:
parent
9d5870478c
commit
c9809eea86
|
@ -16,6 +16,13 @@ myst:
|
|||
|
||||
## Unreleased
|
||||
|
||||
- {{ Enhancement }} `str(jsproxy)` has been adjusted to not raise an error if
|
||||
`jsproxy.toString` is undefined. Instead, it will use
|
||||
`Object.prototype.toString` in this case. If `jsproxy.toString` is defined and
|
||||
throws or is not defined but `jsproxy[Symbol.toStringTag]` is defined and
|
||||
throws, then `str` will still raise.
|
||||
{pr}`4574`
|
||||
|
||||
- {{ Enhancement }} Improved support for stack switching.
|
||||
{pr}`4532`, {pr}`4547`
|
||||
|
||||
|
|
|
@ -279,7 +279,10 @@ EM_JS_VAL(JsVal, JsvObject_Values, (JsVal obj), {
|
|||
|
||||
EM_JS_VAL(JsVal,
|
||||
JsvObject_toString, (JsVal obj), {
|
||||
return obj.toString();
|
||||
if (hasMethod(obj, "toString")) {
|
||||
return obj.toString();
|
||||
}
|
||||
return Object.prototype.toString.call(obj);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -269,9 +269,6 @@ JsProxy_Repr(PyObject* self)
|
|||
{
|
||||
JsVal repr = JsvObject_toString(JsProxy_VAL(self));
|
||||
if (JsvNull_Check(repr)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Pyodide cannot generate a repr for this Javascript object "
|
||||
"because it has no 'toString' method");
|
||||
return NULL;
|
||||
}
|
||||
return js2python(repr);
|
||||
|
|
|
@ -1286,13 +1286,10 @@ def test_js_id(selenium):
|
|||
|
||||
@run_in_pyodide
|
||||
def test_object_with_null_constructor(selenium):
|
||||
from unittest import TestCase
|
||||
|
||||
from pyodide.code import run_js
|
||||
|
||||
o = run_js("Object.create(null)")
|
||||
with TestCase().assertRaises(TypeError):
|
||||
repr(o)
|
||||
assert repr(o) == "[object Object]"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("n", [1 << 31, 1 << 32, 1 << 33, 1 << 63, 1 << 64, 1 << 65])
|
||||
|
@ -2557,3 +2554,70 @@ def test_js_proxy_attribute(selenium):
|
|||
assert x.c is None
|
||||
with pytest.raises(AttributeError):
|
||||
x.d # noqa: B018
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
async def test_js_proxy_str(selenium):
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from js import Array
|
||||
from pyodide.code import run_js
|
||||
from pyodide.ffi import JsException
|
||||
|
||||
assert (
|
||||
re.sub(r"\s+", " ", str(Array).replace("\n", " "))
|
||||
== "function Array() { [native code] }"
|
||||
)
|
||||
assert str(run_js("[1,2,3]")) == "1,2,3"
|
||||
assert str(run_js("Object.create(null)")) == "[object Object]"
|
||||
mod = await run_js("import('data:text/javascript,')")
|
||||
assert str(mod) == "[object Module]"
|
||||
# accessing toString fails, should fall back to Object.prototype.toString.call
|
||||
x = run_js(
|
||||
"""
|
||||
({
|
||||
get toString() {
|
||||
throw new Error();
|
||||
},
|
||||
[Symbol.toStringTag] : "SomeTag"
|
||||
})
|
||||
"""
|
||||
)
|
||||
assert str(x) == "[object SomeTag]"
|
||||
# accessing toString succeeds but toString call throws, let exception propagate
|
||||
x = run_js(
|
||||
"""
|
||||
({
|
||||
toString() {
|
||||
throw new Error("hi!");
|
||||
},
|
||||
})
|
||||
"""
|
||||
)
|
||||
with pytest.raises(JsException, match="hi!"):
|
||||
str(x)
|
||||
|
||||
# No toString method, so we fall back to Object.prototype.toString.call
|
||||
# which throws, let error propagate
|
||||
x = run_js(
|
||||
"""
|
||||
({
|
||||
get [Symbol.toStringTag]() {
|
||||
throw new Error("hi!");
|
||||
},
|
||||
});
|
||||
"""
|
||||
)
|
||||
with pytest.raises(JsException, match="hi!"):
|
||||
str(x)
|
||||
|
||||
# accessing toString fails, so fall back to Object.prototype.toString.call
|
||||
# which also throws, let error propagate
|
||||
px = run_js("(p = Proxy.revocable({}, {})); p.revoke(); p.proxy")
|
||||
with pytest.raises(
|
||||
JsException,
|
||||
match="revoked",
|
||||
):
|
||||
str(px)
|
||||
|
|
Loading…
Reference in New Issue