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;
self->heap_manager = NULL; /* We manage the heap */
self->py_thread_state = NULL;
self->ctx = duk_create_heap_default();
if (!self->ctx) {
@ -87,7 +88,7 @@ static void DukContext_dealloc(DukContext *self)
static PyObject *DukContext_eval(DukContext *self, PyObject *args, PyObject *kw)
{
const char *code;
int noresult = 0;
int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = 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 (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);
if (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)
{
const char *path;
int noresult = 0;
int noresult = 0, ret = 0;
PyObject *result = NULL, *temp = 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 (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);
if (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)
{
PyObject *func, *args, *result;
DukContext *dctx;
duk_idx_t nargs, i;
static char buf1[200], buf2[1024];
int gil_acquired = 0, ret = 1;
dctx = DukContext_get(ctx);
nargs = duk_get_top(ctx);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "\xff" "py_object");
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);
if (!args)
return DUK_RET_ALLOC_ERROR;
if (!args) {
ret = DUK_RET_ALLOC_ERROR;
goto error;
}
for (i = 0; i < nargs; i++) {
PyObject *arg = duk_to_python(ctx, i);
if (arg == NULL)
return DUK_RET_TYPE_ERROR;
if (arg == NULL) {
Py_DECREF(args);
ret = DUK_RET_TYPE_ERROR;
goto error;
}
PyTuple_SET_ITEM(args, i, arg);
}
result = PyObject_Call(func, args, NULL);
Py_DECREF(args);
if (!result) {
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);
}
PyObject *ptype = NULL, *pval = NULL, *tb = NULL;
PyErr_Fetch(&ptype, &pval, &tb);
if (!get_repr(pval, buf2, 1024)) get_repr(ptype, buf2, 1024);
Py_XDECREF(ptype); Py_XDECREF(pval); Py_XDECREF(tb);
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);
}
python_to_duk(ctx, 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) {
int deleted = 0;
int deleted = 0, gil_acquired = 0;
DukContext *dctx = DukContext_get(ctx);
duk_get_prop_string(ctx, 0, "\xff""deleted");
deleted = duk_to_boolean(ctx, -1);
duk_pop(ctx);
if (!deleted) {
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));
if (gil_acquired) {
dctx->py_thread_state = PyEval_SaveThread();
gil_acquired = 0;
}
duk_pop(ctx);
// Mark as deleted

View File

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

View File

@ -1,10 +1,12 @@
import os
import sys
import unittest
from threading import Thread, Event
from duktape import dukpy
undefined, JSError, Context = dukpy.undefined, dukpy.JSError, dukpy.Context
class ContextTests(unittest.TestCase):
def setUp(self):
self.ctx = Context()
self.g = self.ctx.g
@ -30,6 +32,7 @@ class ContextTests(unittest.TestCase):
class ValueTests(unittest.TestCase):
def setUp(self):
self.ctx = Context()
self.g = self.ctx.g
@ -106,6 +109,7 @@ class ValueTests(unittest.TestCase):
class EvalTests(unittest.TestCase):
def setUp(self):
self.ctx = Context()
self.g = self.ctx.g
@ -148,6 +152,18 @@ class EvalTests(unittest.TestCase):
self.assertEqual('ReferenceError', e.name)
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):
self.assertIsNone(self.ctx.eval("1+1", noreturn=True))
@ -166,5 +182,3 @@ class EvalTests(unittest.TestCase):
def test_eval_file_noreturn(self):
self.assertIsNone(self.ctx.eval_file(self.testfile, noreturn=True))