Add descr_get handler to JsMethod (#3130)

This commit is contained in:
Hood Chatham 2022-09-22 15:36:38 -07:00 committed by GitHub
parent a916ea0b34
commit cf3f355306
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 0 deletions

View File

@ -54,6 +54,12 @@ substitutions:
first argument using the {any}`captureThis` method.
{pr}`3103`
- {{ Enhancement }} A `JsProxy` of a function now has a `__get__` descriptor
method, so it's possible to use a JavaScript function as a Python method. When
the method is called, `this` will be a `PyProxy` pointing to the Python object
the method is called on.
{pr}`3130`
### Build System / Package Loading
- New packages: pycryptodomex {pr}`2966`, pycryptodome {pr}`2965`,

View File

@ -103,6 +103,9 @@ typedef struct
static void
JsProxy_dealloc(JsProxy* self)
{
if (pyproxy_Check(self->this_)) {
destroy_proxy(self->this_, NULL);
}
#ifdef DEBUG_F
extern bool tracerefs;
if (tracerefs) {
@ -1897,6 +1900,26 @@ static PyMethodDef JsMethod_Construct_MethodDef = {
};
// clang-format on
static PyObject*
JsMethod_descr_get(PyObject* self, PyObject* obj, PyObject* type)
{
JsRef jsobj = NULL;
PyObject* result = NULL;
if (obj == Py_None || obj == NULL) {
Py_INCREF(self);
return self;
}
jsobj = python2js(obj);
FAIL_IF_NULL(jsobj);
result = JsProxy_create_with_this(JsProxy_REF(self), jsobj);
finally:
hiwire_CLEAR(jsobj);
return result;
}
static int
JsMethod_cinit(PyObject* obj, JsRef this_)
{
@ -2445,6 +2468,8 @@ JsProxy_create_subtype(int flags)
tp_flags |= _Py_TPFLAGS_HAVE_VECTORCALL;
slots[cur_slot++] =
(PyType_Slot){ .slot = Py_tp_call, .pfunc = (void*)PyVectorcall_Call };
slots[cur_slot++] = (PyType_Slot){ .slot = Py_tp_descr_get,
.pfunc = (void*)JsMethod_descr_get };
// We could test separately for whether a function is constructable,
// but it generates a lot of false positives.
methods[cur_method++] = JsMethod_Construct_MethodDef;

View File

@ -1276,3 +1276,20 @@ def test_jsarray_reverse(selenium):
assert a.to_py() == l
assert b.to_bytes() == bytes(l)
@run_in_pyodide
def test_jsproxy_descr_get(selenium):
from pyodide.code import run_js
class T:
a: int
b: int
f = run_js("function f(x) {return this[x]; }; f")
t = T()
t.a = 7
t.b = 66
assert t.f("a") == 7
assert t.f("b") == 66
assert t.f("c") is None