mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Update duktape python bindings
This commit is contained in:
parent
2368db92d6
commit
5d3d892923
@ -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);
|
||||
|
@ -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
|
||||
|
@ -22,6 +22,7 @@ struct DukContext_ {
|
||||
PyObject_HEAD
|
||||
duk_context *ctx;
|
||||
DukContext *heap_manager;
|
||||
PyThreadState *py_thread_state;
|
||||
};
|
||||
|
||||
PyTypeObject DukContext_Type;
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user