Update duktape python bindings

This commit is contained in:
Kovid Goyal 2015-06-18 19:15:26 +05:30
parent 2368db92d6
commit 5d3d892923
4 changed files with 79 additions and 13 deletions

View File

@ -18,6 +18,7 @@ static int DukContext_init(DukContext *self, PyObject *args, PyObject *kw)
(void)kw; (void)kw;
self->heap_manager = NULL; /* We manage the heap */ self->heap_manager = NULL; /* We manage the heap */
self->py_thread_state = NULL;
self->ctx = duk_create_heap_default(); self->ctx = duk_create_heap_default();
if (!self->ctx) { if (!self->ctx) {
@ -87,7 +88,7 @@ static void DukContext_dealloc(DukContext *self)
static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw) static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw)
{ {
const char *code; const char *code;
int noresult = 0; int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = NULL; PyObject *result = NULL, *temp = NULL;
static char *keywords[] = {"code", "noreturn", NULL}; static char *keywords[] = {"code", "noreturn", NULL};
@ -97,7 +98,11 @@ static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw)
} }
if (temp && PyObject_IsTrue(temp)) noresult = 1; if (temp && PyObject_IsTrue(temp)) noresult = 1;
if (duk_peval_string(self->ctx, code) != 0) { self->py_thread_state = PyEval_SaveThread(); // Release GIL
ret = duk_peval_string(self->ctx, code);
PyEval_RestoreThread(self->py_thread_state); // Acquire GIL
self->py_thread_state = NULL;
if (ret != 0) {
temp = duk_to_python(self->ctx, -1); temp = duk_to_python(self->ctx, -1);
if (temp) { if (temp) {
PyErr_SetObject(JSError, temp); PyErr_SetObject(JSError, temp);
@ -121,7 +126,7 @@ static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw)
static PyObject *DukContext_eval_file(DukContext *self, PyObject *args, PyObject *kw) static PyObject *DukContext_eval_file(DukContext *self, PyObject *args, PyObject *kw)
{ {
const char *path; const char *path;
int noresult = 0; int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = NULL; PyObject *result = NULL, *temp = NULL;
static char *keywords[] = {"path", "noreturn", NULL}; static char *keywords[] = {"path", "noreturn", NULL};
@ -131,7 +136,11 @@ static PyObject *DukContext_eval_file(DukContext *self, PyObject *args, PyObject
} }
if (temp && PyObject_IsTrue(temp)) noresult = 1; if (temp && PyObject_IsTrue(temp)) noresult = 1;
if (duk_peval_file(self->ctx, path) != 0) { self->py_thread_state = PyEval_SaveThread(); // Release GIL
ret = duk_peval_file(self->ctx, path);
PyEval_RestoreThread(self->py_thread_state); // Acquire GIL
self->py_thread_state = NULL;
if (ret != 0) {
temp = duk_to_python(self->ctx, -1); temp = duk_to_python(self->ctx, -1);
if (temp) { if (temp) {
PyErr_SetObject(JSError, temp); PyErr_SetObject(JSError, temp);

View File

@ -20,53 +20,95 @@ static int get_repr(PyObject *value, char *buf, int bufsz) {
static duk_ret_t python_function_caller(duk_context *ctx) static duk_ret_t python_function_caller(duk_context *ctx)
{ {
PyObject *func, *args, *result; PyObject *func, *args, *result;
DukContext *dctx;
duk_idx_t nargs, i; duk_idx_t nargs, i;
static char buf1[200], buf2[1024]; static char buf1[200], buf2[1024];
int gil_acquired = 0, ret = 1;
dctx = DukContext_get(ctx);
nargs = duk_get_top(ctx); nargs = duk_get_top(ctx);
duk_push_current_function(ctx); duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "\xff" "py_object"); duk_get_prop_string(ctx, -1, "\xff" "py_object");
func = duk_get_pointer(ctx, -1); func = duk_get_pointer(ctx, -1);
if (dctx->py_thread_state) {
gil_acquired = 1;
PyEval_RestoreThread(dctx->py_thread_state);
dctx->py_thread_state = NULL;
}
args = PyTuple_New(nargs); args = PyTuple_New(nargs);
if (!args) if (!args) {
return DUK_RET_ALLOC_ERROR; ret = DUK_RET_ALLOC_ERROR;
goto error;
}
for (i = 0; i < nargs; i++) { for (i = 0; i < nargs; i++) {
PyObject *arg = duk_to_python(ctx, i); PyObject *arg = duk_to_python(ctx, i);
if (arg == NULL) if (arg == NULL) {
return DUK_RET_TYPE_ERROR; Py_DECREF(args);
ret = DUK_RET_TYPE_ERROR;
goto error;
}
PyTuple_SET_ITEM(args, i, arg); PyTuple_SET_ITEM(args, i, arg);
} }
result = PyObject_Call(func, args, NULL); result = PyObject_Call(func, args, NULL);
Py_DECREF(args);
if (!result) { if (!result) {
get_repr(func, buf1, 200); get_repr(func, buf1, 200);
if (!PyErr_Occurred()) if (!PyErr_Occurred()) {
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
duk_error(ctx, DUK_ERR_ERROR, "Python function (%s) failed", buf1); duk_error(ctx, DUK_ERR_ERROR, "Python function (%s) failed", buf1);
}
PyObject *ptype = NULL, *pval = NULL, *tb = NULL; PyObject *ptype = NULL, *pval = NULL, *tb = NULL;
PyErr_Fetch(&ptype, &pval, &tb); PyErr_Fetch(&ptype, &pval, &tb);
if (!get_repr(pval, buf2, 1024)) get_repr(ptype, buf2, 1024); if (!get_repr(pval, buf2, 1024)) get_repr(ptype, buf2, 1024);
Py_XDECREF(ptype); Py_XDECREF(pval); Py_XDECREF(tb); Py_XDECREF(ptype); Py_XDECREF(pval); Py_XDECREF(tb);
PyErr_Clear(); /* In case there was an error in get_repr() */ PyErr_Clear(); /* In case there was an error in get_repr() */
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
duk_error(ctx, DUK_ERR_ERROR, "Python function (%s) failed with error: %s", buf1, buf2); duk_error(ctx, DUK_ERR_ERROR, "Python function (%s) failed with error: %s", buf1, buf2);
} }
python_to_duk(ctx, result); python_to_duk(ctx, result);
Py_DECREF(result); Py_DECREF(result);
return 1; error:
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
return ret;
} }
static duk_ret_t python_object_decref(duk_context *ctx) { static duk_ret_t python_object_decref(duk_context *ctx) {
int deleted = 0; int deleted = 0, gil_acquired = 0;
DukContext *dctx = DukContext_get(ctx);
duk_get_prop_string(ctx, 0, "\xff""deleted"); duk_get_prop_string(ctx, 0, "\xff""deleted");
deleted = duk_to_boolean(ctx, -1); deleted = duk_to_boolean(ctx, -1);
duk_pop(ctx); duk_pop(ctx);
if (!deleted) { if (!deleted) {
duk_get_prop_string(ctx, 0, "\xff""py_object"); duk_get_prop_string(ctx, 0, "\xff""py_object");
if (dctx->py_thread_state) {
gil_acquired = 1;
PyEval_RestoreThread(dctx->py_thread_state);
dctx->py_thread_state = NULL;
}
Py_XDECREF(duk_get_pointer(ctx, -1)); Py_XDECREF(duk_get_pointer(ctx, -1));
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
duk_pop(ctx); duk_pop(ctx);
// Mark as deleted // Mark as deleted

View File

@ -22,6 +22,7 @@ struct DukContext_ {
PyObject_HEAD PyObject_HEAD
duk_context *ctx; duk_context *ctx;
DukContext *heap_manager; DukContext *heap_manager;
PyThreadState *py_thread_state;
}; };
PyTypeObject DukContext_Type; PyTypeObject DukContext_Type;

View File

@ -1,10 +1,12 @@
import os import os
import sys import sys
import unittest import unittest
from threading import Thread, Event
from duktape import dukpy from duktape import dukpy
undefined, JSError, Context = dukpy.undefined, dukpy.JSError, dukpy.Context undefined, JSError, Context = dukpy.undefined, dukpy.JSError, dukpy.Context
class ContextTests(unittest.TestCase): class ContextTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.ctx = Context() self.ctx = Context()
self.g = self.ctx.g self.g = self.ctx.g
@ -30,6 +32,7 @@ class ContextTests(unittest.TestCase):
class ValueTests(unittest.TestCase): class ValueTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.ctx = Context() self.ctx = Context()
self.g = self.ctx.g self.g = self.ctx.g
@ -106,6 +109,7 @@ class ValueTests(unittest.TestCase):
class EvalTests(unittest.TestCase): class EvalTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.ctx = Context() self.ctx = Context()
self.g = self.ctx.g self.g = self.ctx.g
@ -148,6 +152,18 @@ class EvalTests(unittest.TestCase):
self.assertEqual('ReferenceError', e.name) self.assertEqual('ReferenceError', e.name)
self.assertEqual(2, e.lineNumber) self.assertEqual(2, e.lineNumber)
def test_eval_multithreading(self):
ev = Event()
self.ctx.g.func = ev.wait
t = Thread(target=self.ctx.eval, args=('func()',))
t.daemon = True
t.start()
t.join(0.01)
self.assertTrue(t.is_alive())
ev.set()
t.join(1)
self.assertFalse(t.is_alive())
def test_eval_noreturn(self): def test_eval_noreturn(self):
self.assertIsNone(self.ctx.eval("1+1", noreturn=True)) self.assertIsNone(self.ctx.eval("1+1", noreturn=True))
@ -166,5 +182,3 @@ class EvalTests(unittest.TestCase):
def test_eval_file_noreturn(self): def test_eval_file_noreturn(self):
self.assertIsNone(self.ctx.eval_file(self.testfile, noreturn=True)) self.assertIsNone(self.ctx.eval_file(self.testfile, noreturn=True))