mirror of https://github.com/pyodide/pyodide.git
Use proper duck typing for JsProxy (#1186)
This commit is contained in:
parent
29639e0541
commit
ab249a0a50
|
@ -12,7 +12,10 @@ except ImportError:
|
|||
|
||||
class _module:
|
||||
class packages:
|
||||
dependencies = [] # type: ignore
|
||||
class dependencies:
|
||||
@staticmethod
|
||||
def object_entries():
|
||||
return []
|
||||
|
||||
|
||||
import hashlib
|
||||
|
@ -140,7 +143,9 @@ class _PackageManager:
|
|||
|
||||
def __init__(self):
|
||||
self.builtin_packages = {}
|
||||
self.builtin_packages.update(js_pyodide._module.packages.dependencies)
|
||||
self.builtin_packages.update(
|
||||
js_pyodide._module.packages.dependencies.object_entries()
|
||||
)
|
||||
self.installed_packages = {}
|
||||
|
||||
def install(
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
#include "hiwire.h"
|
||||
|
||||
#define ERROR_REF (0)
|
||||
#define ERROR_NUM (-1)
|
||||
|
||||
const JsRef Js_undefined = ((JsRef)(2));
|
||||
const JsRef Js_true = ((JsRef)(4));
|
||||
const JsRef Js_false = ((JsRef)(6));
|
||||
|
@ -217,6 +220,18 @@ EM_JS(void _Py_NO_RETURN, hiwire_throw_error, (JsRef iderr), {
|
|||
throw Module.hiwire.pop_value(iderr);
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_is_array, (JsRef idobj), {
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
if (Array.isArray(obj)) {
|
||||
return true;
|
||||
}
|
||||
let result = Object.prototype.toString.call(obj);
|
||||
// We want to treat some standard array-like objects as Array.
|
||||
// clang-format off
|
||||
return result === "[object HTMLCollection]" || result === "[object NodeList]";
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_array, (), { return Module.hiwire.new_value([]); });
|
||||
|
||||
EM_JS_NUM(errcode, hiwire_push_array, (JsRef idarr, JsRef idval), {
|
||||
|
@ -237,21 +252,25 @@ EM_JS_NUM(errcode,
|
|||
|
||||
EM_JS_REF(JsRef, hiwire_get_global, (const char* ptrname), {
|
||||
let jsname = UTF8ToString(ptrname);
|
||||
if (jsname in self) {
|
||||
return Module.hiwire.new_value(self[jsname]);
|
||||
} else {
|
||||
return Module.hiwire.ERROR;
|
||||
let result = globalThis[jsname];
|
||||
// clang-format off
|
||||
if (result === undefined && !(jsname in globalThis)) {
|
||||
// clang-format on
|
||||
return ERROR_REF;
|
||||
}
|
||||
return Module.hiwire.new_value(result);
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_get_member_string, (JsRef idobj, const char* ptrkey), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
let jskey = UTF8ToString(ptrkey);
|
||||
if (jskey in jsobj) {
|
||||
return Module.hiwire.new_value(jsobj[jskey]);
|
||||
} else {
|
||||
return Module.hiwire.ERROR;
|
||||
let result = jsobj[jskey];
|
||||
// clang-format off
|
||||
if (result === undefined && !(jskey in jsobj)) {
|
||||
// clang-format on
|
||||
return ERROR_REF;
|
||||
}
|
||||
return Module.hiwire.new_value(result);
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode,
|
||||
|
@ -274,22 +293,40 @@ EM_JS_NUM(errcode,
|
|||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_get_member_int, (JsRef idobj, int idx), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
return Module.hiwire.new_value(jsobj[idx]);
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let result = obj[idx];
|
||||
// clang-format off
|
||||
if (result === undefined && !(idx in obj)) {
|
||||
// clang-format on
|
||||
return ERROR_REF;
|
||||
}
|
||||
return Module.hiwire.new_value(result);
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode, hiwire_set_member_int, (JsRef idobj, int idx, JsRef idval), {
|
||||
Module.hiwire.get_value(idobj)[idx] = Module.hiwire.get_value(idval);
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode, hiwire_delete_member_int, (JsRef idobj, int idx), {
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
// Weird edge case: allow deleting an empty entry, but we raise a key error if
|
||||
// access is attempted.
|
||||
if (idx < 0 || idx >= obj.length) {
|
||||
return ERROR_NUM;
|
||||
}
|
||||
obj.splice(idx, 1);
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_get_member_obj, (JsRef idobj, JsRef ididx), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
let jsidx = Module.hiwire.get_value(ididx);
|
||||
if (jsidx in jsobj) {
|
||||
return Module.hiwire.new_value(jsobj[jsidx]);
|
||||
} else {
|
||||
return Module.hiwire.ERROR;
|
||||
let result = jsobj[jsidx];
|
||||
// clang-format off
|
||||
if (result === undefined && !(jsidx in jsobj)) {
|
||||
// clang-format on
|
||||
return ERROR_REF;
|
||||
}
|
||||
return Module.hiwire.new_value(result);
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode,
|
||||
|
@ -362,8 +399,25 @@ EM_JS_REF(JsRef, hiwire_new, (JsRef idobj, JsRef idargs), {
|
|||
return Module.hiwire.new_value(Reflect.construct(jsobj, jsargs));
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_has_length, (JsRef idobj), {
|
||||
let val = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
return (typeof val.size === "number") ||
|
||||
(typeof val.length === "number" && typeof val !== "function");
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(int, hiwire_get_length, (JsRef idobj), {
|
||||
return Module.hiwire.get_value(idobj).length;
|
||||
let val = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
if (typeof val.size === "number") {
|
||||
return val.size;
|
||||
}
|
||||
if (typeof val.length === "number") {
|
||||
return val.length;
|
||||
}
|
||||
// clang-format on
|
||||
return ERROR_NUM;
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_get_bool, (JsRef idobj), {
|
||||
|
@ -383,12 +437,88 @@ EM_JS_NUM(bool, hiwire_get_bool, (JsRef idobj), {
|
|||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_is_pyproxy, (JsRef idobj), {
|
||||
EM_JS_NUM(bool, hiwire_has_has_method, (JsRef idobj), {
|
||||
// clang-format off
|
||||
return Module.PyProxy.isPyProxy(Module.hiwire.get_value(idobj));
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
return obj && typeof obj.has === "function";
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_call_has_method, (JsRef idobj, JsRef idkey), {
|
||||
// clang-format off
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let key = Module.hiwire.get_value(idkey);
|
||||
return obj.has(key);
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_has_includes_method, (JsRef idobj), {
|
||||
// clang-format off
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
return obj && typeof obj.includes === "function";
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_call_includes_method, (JsRef idobj, JsRef idval), {
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let val = Module.hiwire.get_value(idval);
|
||||
return obj.includes(val);
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_has_get_method, (JsRef idobj), {
|
||||
// clang-format off
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
return obj && typeof obj.get === "function";
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_call_get_method, (JsRef idobj, JsRef idkey), {
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let key = Module.hiwire.get_value(idkey);
|
||||
let result = obj.get(key);
|
||||
// clang-format off
|
||||
if (result === undefined) {
|
||||
// Try to distinguish between undefined and missing:
|
||||
// If the object has a "has" method and it returns false for this key, the
|
||||
// key is missing. Otherwise, assume key present and value was undefined.
|
||||
// TODO: in absence of a "has" method, should we return None or KeyError?
|
||||
if (obj.has && typeof obj.has === "function" && !obj.has(key)) {
|
||||
return ERROR_REF;
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
return Module.hiwire.new_value(result);
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_has_set_method, (JsRef idobj), {
|
||||
// clang-format off
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
return obj && typeof obj.set === "function";
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode,
|
||||
hiwire_call_set_method,
|
||||
(JsRef idobj, JsRef idkey, JsRef idval),
|
||||
{
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let key = Module.hiwire.get_value(idkey);
|
||||
let val = Module.hiwire.get_value(idval);
|
||||
let result = obj.set(key, val);
|
||||
});
|
||||
|
||||
EM_JS_NUM(errcode, hiwire_call_delete_method, (JsRef idobj, JsRef idkey), {
|
||||
let obj = Module.hiwire.get_value(idobj);
|
||||
let key = Module.hiwire.get_value(idkey);
|
||||
if (!obj.delete(key)) {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_is_pyproxy, (JsRef idobj), {
|
||||
return Module.PyProxy.isPyProxy(Module.hiwire.get_value(idobj));
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_is_function, (JsRef idobj), {
|
||||
// clang-format off
|
||||
return typeof Module.hiwire.get_value(idobj) === 'function';
|
||||
|
@ -448,34 +578,40 @@ MAKE_OPERATOR(not_equal, !==);
|
|||
MAKE_OPERATOR(greater_than, >);
|
||||
MAKE_OPERATOR(greater_than_equal, >=);
|
||||
|
||||
EM_JS_REF(int, hiwire_next, (JsRef idobj, JsRef* result_ptr), {
|
||||
// clang-format off
|
||||
EM_JS_REF(JsRef, hiwire_is_iterator, (JsRef idobj), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
return typeof jsobj.next === 'function';
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_NUM(int, hiwire_next, (JsRef idobj, JsRef* result_ptr), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
let { done, value } = jsobj.next();
|
||||
// clang-format on
|
||||
let result_id = Module.hiwire.new_value(value);
|
||||
setValue(result_ptr, result_id, "i32");
|
||||
return done;
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_is_iterable, (JsRef idobj), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
return typeof jsobj[Symbol.iterator] === 'function';
|
||||
// clang-format on
|
||||
});
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_get_iterator, (JsRef idobj), {
|
||||
// clang-format off
|
||||
if (idobj === Module.hiwire.UNDEFINED) {
|
||||
return Module.hiwire.ERROR;
|
||||
}
|
||||
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
if (typeof jsobj.next === 'function') {
|
||||
return Module.hiwire.new_value(jsobj);
|
||||
} else if (typeof jsobj[Symbol.iterator] === 'function') {
|
||||
return Module.hiwire.new_value(jsobj[Symbol.iterator]());
|
||||
} else {
|
||||
return Module.hiwire.new_value(Object.entries(jsobj)[Symbol.iterator]());
|
||||
}
|
||||
return Module.hiwire.ERROR;
|
||||
// clang-format on
|
||||
return Module.hiwire.new_value(jsobj[Symbol.iterator]());
|
||||
})
|
||||
|
||||
EM_JS_REF(JsRef, hiwire_object_entries, (JsRef idobj), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
return Module.hiwire.new_value(Object.entries(jsobj));
|
||||
});
|
||||
|
||||
EM_JS_NUM(bool, hiwire_is_typedarray, (JsRef idobj), {
|
||||
let jsobj = Module.hiwire.get_value(idobj);
|
||||
// clang-format off
|
||||
|
@ -541,7 +677,7 @@ EM_JS_REF(JsRef, hiwire_subarray, (JsRef idarr, int start, int end), {
|
|||
return Module.hiwire.new_value(jssub);
|
||||
});
|
||||
|
||||
EM_JS_NUM(JsRef, JsMap_New, (), { return Module.hiwire.new_value(new Map()); })
|
||||
EM_JS_REF(JsRef, JsMap_New, (), { return Module.hiwire.new_value(new Map()); })
|
||||
|
||||
EM_JS_NUM(errcode, JsMap_Set, (JsRef mapid, JsRef keyid, JsRef valueid), {
|
||||
let map = Module.hiwire.get_value(mapid);
|
||||
|
@ -550,7 +686,7 @@ EM_JS_NUM(errcode, JsMap_Set, (JsRef mapid, JsRef keyid, JsRef valueid), {
|
|||
map.set(key, value);
|
||||
})
|
||||
|
||||
EM_JS_NUM(JsRef, JsSet_New, (), { return Module.hiwire.new_value(new Set()); })
|
||||
EM_JS_REF(JsRef, JsSet_New, (), { return Module.hiwire.new_value(new Set()); })
|
||||
|
||||
EM_JS_NUM(errcode, JsSet_Add, (JsRef mapid, JsRef keyid), {
|
||||
let set = Module.hiwire.get_value(mapid);
|
||||
|
|
|
@ -240,6 +240,9 @@ hiwire_float64array(f64* ptr, int len);
|
|||
JsRef
|
||||
hiwire_bool(bool boolean);
|
||||
|
||||
bool
|
||||
hiwire_is_array(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Create a new Javascript Array.
|
||||
*
|
||||
|
@ -306,7 +309,6 @@ hiwire_set_member_string(JsRef idobj, const char* ptrname, JsRef idval);
|
|||
|
||||
/**
|
||||
* Delete an object member by string.
|
||||
*
|
||||
*/
|
||||
errcode
|
||||
hiwire_delete_member_string(JsRef idobj, const char* ptrname);
|
||||
|
@ -314,8 +316,6 @@ hiwire_delete_member_string(JsRef idobj, const char* ptrname);
|
|||
/**
|
||||
* Get an object member by integer.
|
||||
*
|
||||
* The integer is a C integer, not an id reference to a Javascript integer.
|
||||
*
|
||||
* Returns: New reference
|
||||
*/
|
||||
JsRef
|
||||
|
@ -323,13 +323,13 @@ hiwire_get_member_int(JsRef idobj, int idx);
|
|||
|
||||
/**
|
||||
* Set an object member by integer.
|
||||
*
|
||||
* The integer is a C integer, not an id reference to a Javascript integer.
|
||||
*
|
||||
*/
|
||||
errcode
|
||||
hiwire_set_member_int(JsRef idobj, int idx, JsRef idval);
|
||||
|
||||
errcode
|
||||
hiwire_delete_member_int(JsRef idobj, int idx);
|
||||
|
||||
/**
|
||||
* Get an object member by object.
|
||||
*
|
||||
|
@ -398,35 +398,112 @@ JsRef
|
|||
hiwire_new(JsRef idobj, JsRef idargs);
|
||||
|
||||
/**
|
||||
* Returns the value of the `length` member on a Javascript object.
|
||||
*
|
||||
* Returns: C int
|
||||
* Test if the object has a `size` or `length` member which is a number. As a
|
||||
* special case, if the object is a function the `length` field is ignored.
|
||||
*/
|
||||
bool
|
||||
hiwire_has_length(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns the value of the `size` or `length` member on a Javascript object.
|
||||
* Prefers the `size` member if present and a number to the `length` field. If
|
||||
* both `size` and `length` are missing or not a number, returns `-1` to
|
||||
* indicate error.
|
||||
*/
|
||||
int
|
||||
hiwire_get_length(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns the boolean value of a Javascript object.
|
||||
*
|
||||
* Returns: C int
|
||||
*/
|
||||
bool
|
||||
hiwire_get_bool(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Check whether `typeof obj.has === "function"`
|
||||
*/
|
||||
bool
|
||||
hiwire_has_has_method(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Does `obj.has(val)`. Doesn't check type of return value, if it isn't a
|
||||
* boolean or an integer it will get coerced to false.
|
||||
*/
|
||||
bool
|
||||
hiwire_call_has_method(JsRef idobj, JsRef idval);
|
||||
|
||||
/**
|
||||
* Check whether `typeof obj.includes === "function"`.
|
||||
*/
|
||||
bool
|
||||
hiwire_has_includes_method(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Does `obj.includes(val)`. Doesn't check type of return value, if it isn't a
|
||||
* boolean or an integer it will get coerced to `false`.
|
||||
*/
|
||||
bool
|
||||
hiwire_call_includes_method(JsRef idobj, JsRef idval);
|
||||
|
||||
/**
|
||||
* Check whether `typeof obj.get === "function"`.
|
||||
*/
|
||||
bool
|
||||
hiwire_has_get_method(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Call `obj.get(key)`. If the result is `undefined`, we check for a `has`
|
||||
* method and if one is present call `obj.has(key)`. If this returns false we
|
||||
* return `NULL` to signal a `KeyError` otherwise we return `Js_Undefined`. If
|
||||
* no `has` method is present, we return `Js_Undefined`.
|
||||
*/
|
||||
JsRef
|
||||
hiwire_call_get_method(JsRef idobj, JsRef idkey);
|
||||
|
||||
/**
|
||||
* Check whether `typeof obj.set === "function"`.
|
||||
*/
|
||||
bool
|
||||
hiwire_has_set_method(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Call `obj.set(key, value)`. Javascript standard is that `set` returns `false`
|
||||
* to indicate an error condition, but we ignore the return value.
|
||||
*/
|
||||
errcode
|
||||
hiwire_call_set_method(JsRef idobj, JsRef idkey, JsRef idval);
|
||||
|
||||
/**
|
||||
* Call `obj.delete(key)`. Javascript standard is that `delete` returns `false`
|
||||
* to indicate an error condition, if `false` is returned we return `-1` to
|
||||
* indicate the error.
|
||||
*/
|
||||
errcode
|
||||
hiwire_call_delete_method(JsRef idobj, JsRef idkey);
|
||||
|
||||
/**
|
||||
* Check whether the object is a PyProxy.
|
||||
*/
|
||||
bool
|
||||
hiwire_is_pyproxy(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns 1 if the object is a function.
|
||||
*
|
||||
* Returns: C int
|
||||
* Check if the object is a function.
|
||||
*/
|
||||
bool
|
||||
hiwire_is_function(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Check if the object is an error.
|
||||
*/
|
||||
bool
|
||||
hiwire_is_error(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Check if the function supports kwargs. A fairly involved check which parses
|
||||
* func.toString() to determine if the last argument does object destructuring.
|
||||
* Actual implementation in pyodide.js.
|
||||
*/
|
||||
bool
|
||||
hiwire_function_supports_kwargs(JsRef idfunc);
|
||||
|
||||
|
@ -453,7 +530,7 @@ JsRef
|
|||
hiwire_to_string(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Gets the "typeof" string for a value.
|
||||
* Gets the `typeof` string for a value.
|
||||
*
|
||||
* Returns: New reference to Javascript string
|
||||
*/
|
||||
|
@ -461,7 +538,7 @@ JsRef
|
|||
hiwire_typeof(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Gets "value.constructor.name".
|
||||
* Gets `value.constructor.name`.
|
||||
*
|
||||
* Returns: New reference to Javascript string
|
||||
*/
|
||||
|
@ -504,21 +581,40 @@ hiwire_greater_than(JsRef ida, JsRef idb);
|
|||
bool
|
||||
hiwire_greater_than_equal(JsRef ida, JsRef idb);
|
||||
|
||||
/**
|
||||
* Check if `typeof obj.next === "function"`
|
||||
*/
|
||||
JsRef
|
||||
hiwire_is_iterator(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Calls the `next` function on an iterator.
|
||||
*
|
||||
* Returns -1 if an error occurs.
|
||||
* Stores "value" into argument "result", returns "done".
|
||||
* Returns -1 if an error occurs. Otherwise, `next` should return an object with
|
||||
* `value` and `done` fields. We store `value` into the argument `result` and
|
||||
* return `done`.
|
||||
*/
|
||||
int
|
||||
hiwire_next(JsRef idobj, JsRef* result);
|
||||
|
||||
/**
|
||||
* Check if `typeof obj[Symbol.iterator] === "function"`
|
||||
*/
|
||||
JsRef
|
||||
hiwire_is_iterable(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns the iterator associated with the given object, if any.
|
||||
*/
|
||||
JsRef
|
||||
hiwire_get_iterator(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns `Object.entries(obj)`
|
||||
*/
|
||||
JsRef
|
||||
hiwire_object_entries(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns 1 if the value is a typedarray.
|
||||
*/
|
||||
|
@ -532,10 +628,10 @@ bool
|
|||
hiwire_is_on_wasm_heap(JsRef idobj);
|
||||
|
||||
/**
|
||||
* Returns the value of obj.byteLength.
|
||||
* Returns the value of `obj.byteLength`.
|
||||
*
|
||||
* There is no error checking. Caller must ensure that hiwire_is_typedarray is
|
||||
* true.
|
||||
* true. If these conditions are not met, returns `0`.
|
||||
*/
|
||||
int
|
||||
hiwire_get_byteLength(JsRef idobj);
|
||||
|
@ -544,7 +640,8 @@ hiwire_get_byteLength(JsRef idobj);
|
|||
* Returns the value of obj.byteOffset.
|
||||
*
|
||||
* There is no error checking. Caller must ensure that hiwire_is_typedarray is
|
||||
* true and hiwire_is_on_wasm_heap is true.
|
||||
* true and hiwire_is_on_wasm_heap is true. If these conditions are not met,
|
||||
* returns `0`.
|
||||
*/
|
||||
int
|
||||
hiwire_get_byteOffset(JsRef idobj);
|
||||
|
@ -568,15 +665,27 @@ hiwire_get_dtype(JsRef idobj, char** format_ptr, Py_ssize_t* size_ptr);
|
|||
JsRef
|
||||
hiwire_subarray(JsRef idarr, int start, int end);
|
||||
|
||||
/**
|
||||
* Create a new Map.
|
||||
*/
|
||||
JsRef
|
||||
JsMap_New();
|
||||
|
||||
/**
|
||||
* Does map.set(key, value).
|
||||
*/
|
||||
errcode
|
||||
JsMap_Set(JsRef mapid, JsRef keyid, JsRef valueid);
|
||||
|
||||
/**
|
||||
* Create a new Set.
|
||||
*/
|
||||
JsRef
|
||||
JsSet_New();
|
||||
|
||||
/**
|
||||
* Does set.add(key).
|
||||
*/
|
||||
errcode
|
||||
JsSet_Add(JsRef mapid, JsRef keyid);
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ PyObject*
|
|||
_js2python_memoryview(JsRef id)
|
||||
{
|
||||
PyObject* jsproxy = JsProxy_create(id);
|
||||
if (jsproxy == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return PyMemoryView_FromObject(jsproxy);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,9 @@
|
|||
PyObject*
|
||||
JsProxy_create(JsRef v);
|
||||
|
||||
PyObject*
|
||||
JsProxy_create_with_this(JsRef object, JsRef this);
|
||||
|
||||
/** Check if a Python object is a JsProxy object.
|
||||
* \param x The Python object
|
||||
* \return true if the object is a JsProxy object.
|
||||
|
|
|
@ -231,7 +231,8 @@ _python2js_bytes(PyObject* x)
|
|||
// and PySequence_Check returns 1 for classes with a __getitem__ method that
|
||||
// don't subclass dict. For this reason, I think we should stick to subclasses.
|
||||
|
||||
/** WARNING: This function is not suitable for fallbacks. If this function
|
||||
/**
|
||||
* WARNING: This function is not suitable for fallbacks. If this function
|
||||
* returns NULL, we must assume that the cache has been corrupted and bail out.
|
||||
*/
|
||||
static JsRef
|
||||
|
@ -266,7 +267,8 @@ finally:
|
|||
return jsarray;
|
||||
}
|
||||
|
||||
/** WARNING: This function is not suitable for fallbacks. If this function
|
||||
/**
|
||||
* WARNING: This function is not suitable for fallbacks. If this function
|
||||
* returns NULL, we must assume that the cache has been corrupted and bail out.
|
||||
*/
|
||||
static JsRef
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import platform
|
||||
|
||||
if platform.system() == "Emscripten":
|
||||
from _pyodide_core import JsProxy, JsMethod, JsException, JsBuffer
|
||||
from _pyodide_core import JsProxy, JsException, JsBuffer
|
||||
else:
|
||||
# Can add shims here if we are so inclined.
|
||||
class JsException(Exception):
|
||||
|
@ -17,15 +17,10 @@ else:
|
|||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
class JsMethod:
|
||||
"""A proxy to make it possible to call Javascript bound methods from Python."""
|
||||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
class JsBuffer:
|
||||
"""A proxy to make it possible to call Javascript typed arrays from Python."""
|
||||
|
||||
# Defined in jsproxy.c
|
||||
|
||||
|
||||
__all__ = [JsProxy, JsMethod, JsException]
|
||||
__all__ = [JsProxy, JsException]
|
||||
|
|
|
@ -22,7 +22,6 @@ def test_jsproxy_dir(selenium):
|
|||
"__defineGetter__",
|
||||
"__defineSetter__",
|
||||
"__delattr__",
|
||||
"__delitem__",
|
||||
"constructor",
|
||||
"toString",
|
||||
"typeof",
|
||||
|
@ -88,28 +87,25 @@ def test_jsproxy(selenium):
|
|||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import TEST
|
||||
del TEST.y
|
||||
hasattr(TEST, 'y')"""
|
||||
from js import TEST
|
||||
del TEST.y
|
||||
hasattr(TEST, 'y')
|
||||
"""
|
||||
)
|
||||
is False
|
||||
)
|
||||
selenium.run_js(
|
||||
"""
|
||||
class Point {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
window.TEST = new Point(42, 43);"""
|
||||
window.TEST = new Map([["x", 42], ["y", 43]]);
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import TEST
|
||||
del TEST['y']
|
||||
'y' in TEST"""
|
||||
from js import TEST
|
||||
del TEST['y']
|
||||
'y' in TEST
|
||||
"""
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
@ -134,7 +130,7 @@ def test_jsproxy(selenium):
|
|||
selenium.run(
|
||||
"""
|
||||
from js import TEST
|
||||
dict(TEST) == {'foo': 'bar', 'baz': 'bap'}
|
||||
dict(TEST.object_entries()) == {'foo': 'bar', 'baz': 'bap'}
|
||||
"""
|
||||
)
|
||||
is True
|
||||
|
@ -440,12 +436,11 @@ def test_unregister_jsmodule(selenium):
|
|||
pyodide.registerJsModule("a", a);
|
||||
pyodide.registerJsModule("a", b);
|
||||
pyodide.unregisterJsModule("a")
|
||||
pyodide.runPython(`
|
||||
try:
|
||||
await pyodide.runPythonAsync(`
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
with raises(ImportError):
|
||||
import a
|
||||
assert False
|
||||
except ImportError:
|
||||
pass
|
||||
`)
|
||||
"""
|
||||
)
|
||||
|
@ -508,3 +503,214 @@ def test_register_jsmodule_docs_example(selenium):
|
|||
assert c == 2
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_mixins_feature_presence(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
let fields = [
|
||||
[{ [Symbol.iterator](){} }, "__iter__"],
|
||||
[{ next(){} }, "__next__", "__iter__"],
|
||||
[{ length : 1 }, "__len__"],
|
||||
[{ get(){} }, "__getitem__"],
|
||||
[{ set(){} }, "__setitem__", "__delitem__"],
|
||||
[{ has(){} }, "__contains__"],
|
||||
[{ then(){} }, "__await__"]
|
||||
];
|
||||
|
||||
let test_object = pyodide.runPython(`
|
||||
from js import console
|
||||
def test_object(obj, keys_expected):
|
||||
for [key, expected_val] in keys_expected.object_entries():
|
||||
actual_val = hasattr(obj, key)
|
||||
if actual_val != expected_val:
|
||||
console.log(obj)
|
||||
console.log(key)
|
||||
console.log(actual_val)
|
||||
assert False
|
||||
test_object
|
||||
`);
|
||||
|
||||
for(let flags = 0; flags < (1 << fields.length); flags ++){
|
||||
let o = {};
|
||||
let keys_expected = {};
|
||||
for(let [idx, [obj, ...keys]] of fields.entries()){
|
||||
if(flags & (1<<idx)){
|
||||
Object.assign(o, obj);
|
||||
}
|
||||
for(let key of keys){
|
||||
keys_expected[key] = keys_expected[key] || !!(flags & (1<<idx));
|
||||
}
|
||||
}
|
||||
test_object(o, keys_expected);
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_mixins_calls(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
window.testObjects = {};
|
||||
testObjects.iterable = { *[Symbol.iterator](){
|
||||
yield 3; yield 5; yield 7;
|
||||
} };
|
||||
testObjects.iterator = testObjects.iterable[Symbol.iterator]();
|
||||
testObjects.has_len1 = { length : 7, size : 10 };
|
||||
testObjects.has_len2 = { length : 7 };
|
||||
testObjects.has_get = { get(x){ return x; } };
|
||||
testObjects.has_getset = new Map();
|
||||
testObjects.has_has = { has(x){ return typeof(x) === "string" && x.startsWith("x") } };
|
||||
testObjects.has_includes = { includes(x){ return typeof(x) === "string" && x.startsWith("a") } };
|
||||
testObjects.has_has_includes = {
|
||||
includes(x){ return typeof(x) === "string" && x.startsWith("a") },
|
||||
has(x){ return typeof(x) === "string" && x.startsWith("x") }
|
||||
};
|
||||
testObjects.awaitable = { then(cb){ cb(7); } };
|
||||
|
||||
let result = await pyodide.runPythonAsync(`
|
||||
from js import testObjects as obj
|
||||
result = []
|
||||
result.append(["iterable1", list(iter(obj.iterable)), [3, 5, 7]])
|
||||
result.append(["iterable2", [*obj.iterable], [3, 5, 7]])
|
||||
it = obj.iterator
|
||||
result.append(["iterator", [next(it), next(it), next(it)], [3, 5, 7]])
|
||||
result.append(["has_len1", len(obj.has_len1), 10])
|
||||
result.append(["has_len2", len(obj.has_len2), 7])
|
||||
result.append(["has_get1", obj.has_get[10], 10])
|
||||
result.append(["has_get2", obj.has_get[11], 11])
|
||||
m = obj.has_getset
|
||||
m[1] = 6
|
||||
m[2] = 77
|
||||
m[3] = 9
|
||||
m[2] = 5
|
||||
del m[3]
|
||||
result.append(["has_getset", [x.to_py() for x in m.entries()], [[1, 6], [2, 5]]])
|
||||
result.append(["has_has", [n in obj.has_has for n in ["x9", "a9"]], [True, False]])
|
||||
result.append(["has_includes", [n in obj.has_includes for n in ["x9", "a9"]], [False, True]])
|
||||
result.append(["has_has_includes", [n in obj.has_has_includes for n in ["x9", "a9"]], [True, False]])
|
||||
result.append(["awaitable", await obj.awaitable, 7])
|
||||
result
|
||||
`);
|
||||
return result.toJs();
|
||||
"""
|
||||
)
|
||||
for [desc, a, b] in result:
|
||||
assert a == b, desc
|
||||
|
||||
|
||||
def test_mixins_errors(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = [];
|
||||
window.b = {
|
||||
has(){ return false; },
|
||||
get(){ return undefined; },
|
||||
set(){ return false; },
|
||||
delete(){ return false; },
|
||||
};
|
||||
await pyodide.runPythonAsync(`
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
from js import a, b
|
||||
with raises(IndexError):
|
||||
a[0]
|
||||
with raises(IndexError):
|
||||
del a[0]
|
||||
with raises(KeyError):
|
||||
b[0]
|
||||
with raises(KeyError):
|
||||
del b[0]
|
||||
`);
|
||||
|
||||
window.c = {
|
||||
next(){},
|
||||
length : 1,
|
||||
get(){},
|
||||
set(){},
|
||||
has(){},
|
||||
then(){}
|
||||
};
|
||||
window.d = {
|
||||
[Symbol.iterator](){},
|
||||
};
|
||||
pyodide.runPython("from js import c, d");
|
||||
delete c.next;
|
||||
delete c.length;
|
||||
delete c.get;
|
||||
delete c.set;
|
||||
delete c.has;
|
||||
delete c.then;
|
||||
delete d[Symbol.iterator];
|
||||
await pyodide.runPythonAsync(`
|
||||
from contextlib import contextmanager
|
||||
from unittest import TestCase
|
||||
@contextmanager
|
||||
def raises(exc, match=None):
|
||||
with TestCase().assertRaisesRegex(exc, match) as e:
|
||||
yield e
|
||||
|
||||
from pyodide import JsException
|
||||
msg = "^TypeError:.* is not a function$"
|
||||
with raises(JsException, match=msg):
|
||||
next(c)
|
||||
with raises(JsException, match=msg):
|
||||
iter(d)
|
||||
with raises(TypeError, match="object does not have a valid length"):
|
||||
len(c)
|
||||
with raises(JsException, match=msg):
|
||||
c[0]
|
||||
with raises(JsException, match=msg):
|
||||
c[0] = 7
|
||||
with raises(JsException, match=msg):
|
||||
del c[0]
|
||||
with raises(TypeError, match="can't be used in 'await' expression"):
|
||||
await c
|
||||
`);
|
||||
|
||||
window.l = [0, false, NaN, undefined, null];
|
||||
window.l[6] = 7;
|
||||
await pyodide.runPythonAsync(`
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
from js import l
|
||||
with raises(IndexError):
|
||||
l[10]
|
||||
with raises(IndexError):
|
||||
l[5]
|
||||
assert len(l) == 7
|
||||
l[0]; l[1]; l[2]; l[3]
|
||||
l[4]; l[6]
|
||||
del l[1]
|
||||
with raises(IndexError):
|
||||
l[4]
|
||||
l[5]
|
||||
del l[4]
|
||||
l[3]; l[4]
|
||||
`);
|
||||
|
||||
window.l = [0, false, NaN, undefined, null];
|
||||
window.l[6] = 7;
|
||||
let a = Array.from(window.l.entries());
|
||||
console.log(a);
|
||||
a.splice(5, 1);
|
||||
window.m = new Map(a);
|
||||
console.log(m.size);
|
||||
await pyodide.runPythonAsync(`
|
||||
from js import m
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
with raises(KeyError):
|
||||
m[10]
|
||||
with raises(KeyError):
|
||||
m[5]
|
||||
assert len(m) == 6
|
||||
m[0]; m[1]; m[2]; m[3]
|
||||
m[4]; m[6]
|
||||
del m[1]
|
||||
with raises(KeyError):
|
||||
m[1]
|
||||
assert len(m) == 5
|
||||
`);
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -182,10 +182,12 @@ def test_hiwire_is_promise(selenium):
|
|||
"new Map()",
|
||||
"new Set()",
|
||||
]:
|
||||
assert not selenium.run_js(f"return pyodide._module.hiwire.isPromise({s})")
|
||||
assert selenium.run_js(
|
||||
f"return pyodide._module.hiwire.isPromise({s}) === false;"
|
||||
)
|
||||
|
||||
assert selenium.run_js(
|
||||
"return pyodide._module.hiwire.isPromise(Promise.resolve());"
|
||||
"return pyodide._module.hiwire.isPromise(Promise.resolve()) === true;"
|
||||
)
|
||||
|
||||
assert selenium.run_js(
|
||||
|
|
|
@ -158,12 +158,12 @@ def test_eval_nothing(selenium):
|
|||
|
||||
|
||||
def test_unknown_attribute(selenium):
|
||||
selenium.run(
|
||||
selenium.run_async(
|
||||
"""
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaisesRegex
|
||||
import js
|
||||
try:
|
||||
with raises(AttributeError, "asdf"):
|
||||
js.asdf
|
||||
except AttributeError as e:
|
||||
assert "asdf" in str(e)
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -55,6 +55,8 @@ def test_capture_exception(selenium):
|
|||
run_with_resolve(
|
||||
selenium,
|
||||
"""
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
from js import resolve
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
@ -62,12 +64,9 @@ def test_capture_exception(selenium):
|
|||
raise MyException('oops')
|
||||
|
||||
def capture_exception(fut):
|
||||
try:
|
||||
with raises(MyException):
|
||||
fut.result()
|
||||
except MyException:
|
||||
resolve()
|
||||
else:
|
||||
raise Exception("Expected fut.result() to raise MyException")
|
||||
resolve()
|
||||
import asyncio
|
||||
fut = asyncio.ensure_future(foo(998))
|
||||
fut.add_done_callback(capture_exception)
|
||||
|
@ -131,16 +130,15 @@ def test_asyncio_exception(selenium):
|
|||
run_with_resolve(
|
||||
selenium,
|
||||
"""
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
from js import resolve
|
||||
async def dummy_task():
|
||||
raise ValueError("oops!")
|
||||
async def capture_exception():
|
||||
try:
|
||||
with raises(ValueError):
|
||||
await dummy_task()
|
||||
except ValueError:
|
||||
resolve()
|
||||
else:
|
||||
raise Exception("Expected ValueError")
|
||||
resolve()
|
||||
import asyncio
|
||||
asyncio.ensure_future(capture_exception())
|
||||
""",
|
||||
|
|
Loading…
Reference in New Issue