Fix #3: Wrap Python objects for Javascript usage

This commit is contained in:
Michael Droettboom 2018-02-26 15:25:05 -05:00
parent 64ded33c4f
commit df1c2e2cad
9 changed files with 189 additions and 11 deletions

View File

@ -19,7 +19,7 @@ all: build/pyodide.asm.html build/pyodide.js
build/pyodide.asm.html: src/main.bc src/jsproxy.bc src/js2python.bc src/pylocals.bc \
src/python2js.bc src/runpython.bc root
src/pyproxy.bc src/python2js.bc src/runpython.bc root
$(CC) -s WASM=1 -s EXPORT_NAME="'pyodide'" --bind -o $@ $(filter %.bc,$^) $(LDFLAGS) \
$(foreach d,$(wildcard root/*),--preload-file $d@/$(notdir $d))

View File

@ -4,6 +4,9 @@
using emscripten::val;
static val *Array = NULL;
static val *Object = NULL;
PyObject *jsToPython(val x) {
val xType = x.typeOf();
@ -20,3 +23,51 @@ PyObject *jsToPython(val x) {
return JsProxy_cnew(x);
}
}
PyObject *jsToPythonArgs(val args) {
if (!Array->call<bool>("isArray", args)) {
PyErr_SetString(PyExc_TypeError, "Invalid args");
return NULL;
}
Py_ssize_t n = (Py_ssize_t)args["length"].as<long>();
PyObject *pyargs = PyTuple_New(n);
if (pyargs == NULL) {
return NULL;
}
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *arg = jsToPython(args[i]);
PyTuple_SET_ITEM(pyargs, i, arg);
}
return pyargs;
}
PyObject *jsToPythonKwargs(val kwargs) {
val keys = Object->call<val>("keys", kwargs);
Py_ssize_t n = (Py_ssize_t)keys["length"].as<long>();
PyObject *pykwargs = PyDict_New();
if (pykwargs == NULL) {
return NULL;
}
for (Py_ssize_t i = 0; i < n; ++i) {
PyObject *k = jsToPython(keys[i]);
PyObject *v = jsToPython(kwargs[keys[i]]);
if (PyDict_SetItem(pykwargs, k, v)) {
return NULL;
}
Py_DECREF(k);
Py_DECREF(v);
}
return pykwargs;
}
int jsToPython_Ready() {
Array = new val(val::global("Array"));
Object = new val(val::global("Object"));
return 0;
}

View File

@ -7,5 +7,8 @@
#include <Python.h>
PyObject *jsToPython(emscripten::val x);
PyObject *jsToPythonArgs(emscripten::val args);
PyObject *jsToPythonKwargs(emscripten::val kwargs);
int jsToPython_Ready();
#endif /* JS2PYTHON_H */

View File

@ -7,6 +7,7 @@
#include "jsproxy.hpp"
#include "js2python.hpp"
#include "pylocals.hpp"
#include "pyproxy.hpp"
#include "python2js.hpp"
#include "runpython.hpp"
@ -21,7 +22,12 @@ using emscripten::val;
EMSCRIPTEN_BINDINGS(python) {
emscripten::function("runPython", &runPython);
emscripten::class_<PyObject>("PyObject");
emscripten::class_<Py>("Py")
.function<val>("call", &Py::call)
.function<val>("getattr", &Py::getattr)
.function<void>("setattr", &Py::setattr)
.function<val>("getitem", &Py::getitem)
.function<void>("setitem", &Py::setitem);
}
extern "C" {
@ -31,6 +37,7 @@ extern "C" {
Py_InitializeEx(0);
if (JsProxy_Ready() ||
jsToPython_Ready() ||
pythonToJs_Ready() ||
PyLocals_Ready()) {
return 1;

89
src/pyproxy.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "pyproxy.hpp"
using emscripten::val;
Py::Py(PyObject *obj) : x(obj) {
Py_INCREF(x);
}
Py::Py(const Py& o) : x(o.x) {
Py_INCREF(x);
}
Py::~Py() {
Py_DECREF(x);
}
val Py::call(val args, val kwargs) {
PyObject *pyargs = jsToPythonArgs(args);
if (pyargs == NULL) {
return pythonExcToJs();
}
PyObject *pykwargs = jsToPythonKwargs(kwargs);
if (pykwargs == NULL) {
Py_DECREF(pyargs);
return pythonExcToJs();
}
PyObject *pyret = PyObject_Call(x, pyargs, pykwargs);
Py_DECREF(pyargs);
Py_DECREF(pykwargs);
if (pyret == NULL) {
return pythonExcToJs();
}
val ret = pythonToJs(pyret);
Py_DECREF(pyret);
return ret;
}
val Py::getattr(val idx) {
PyObject *pyidx = jsToPython(idx);
PyObject *attr = PyObject_GetAttr(x, pyidx);
Py_DECREF(pyidx);
if (attr == NULL) {
return pythonExcToJs();
}
val ret = pythonToJs(attr);
Py_DECREF(attr);
return ret;
}
void Py::setattr(val idx, val v) {
PyObject *pyidx = jsToPython(idx);
PyObject *pyv = jsToPython(v);
int ret = PyObject_SetAttr(x, pyidx, pyv);
Py_DECREF(pyidx);
Py_DECREF(pyv);
if (ret) {
pythonExcToJs();
}
}
val Py::getitem(val idx) {
PyObject *pyidx = jsToPython(idx);
PyObject *item = PyObject_GetItem(x, pyidx);
Py_DECREF(pyidx);
if (item == NULL) {
return pythonExcToJs();
}
val ret = pythonToJs(item);
Py_DECREF(item);
return ret;
}
void Py::setitem(val idx, val v) {
PyObject *pyidx = jsToPython(idx);
PyObject *pyv = jsToPython(v);
int ret = PyObject_SetItem(x, pyidx, pyv);
Py_DECREF(pyidx);
Py_DECREF(pyv);
if (ret) {
pythonExcToJs();
}
}

27
src/pyproxy.hpp Normal file
View File

@ -0,0 +1,27 @@
#ifndef PYPROXY_H
#define PYPROXY_H
#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <Python.h>
#include "js2python.hpp"
#include "python2js.hpp"
class Py {
PyObject *x;
public:
Py(PyObject *obj);
Py(const Py& o);
~Py();
emscripten::val call(emscripten::val args, emscripten::val kwargs);
emscripten::val getattr(emscripten::val idx);
void setattr(emscripten::val idx, emscripten::val v);
emscripten::val getitem(emscripten::val idx);
void setitem(emscripten::val idx, emscripten::val v);
};
#endif /* PYPROXY_H */

View File

@ -1,11 +1,12 @@
#include "python2js.hpp"
#include "jsproxy.hpp"
#include "pyproxy.hpp"
using emscripten::val;
static val *undefined;
val pythonExcToJS() {
val pythonExcToJs() {
PyObject *type;
PyObject *value;
PyObject *traceback;
@ -36,13 +37,13 @@ val pythonToJs(PyObject *x) {
} else if (PyLong_Check(x)) {
long x_long = PyLong_AsLongLong(x);
if (x_long == -1 && PyErr_Occurred()) {
return pythonExcToJS();
return pythonExcToJs();
}
return val(x_long);
} else if (PyFloat_Check(x)) {
double x_double = PyFloat_AsDouble(x);
if (x_double == -1.0 && PyErr_Occurred()) {
return pythonExcToJS();
return pythonExcToJs();
}
return val(x_double);
} else if (PyUnicode_Check(x)) {
@ -51,7 +52,7 @@ val pythonToJs(PyObject *x) {
Py_ssize_t length;
wchar_t *chars = PyUnicode_AsWideCharString(x, &length);
if (chars == NULL) {
return pythonExcToJS();
return pythonExcToJs();
}
std::wstring x_str(chars, length);
PyMem_Free(chars);
@ -72,7 +73,7 @@ val pythonToJs(PyObject *x) {
for (size_t i = 0; i < length; ++i) {
PyObject *item = PySequence_GetItem(x, i);
if (item == NULL) {
return pythonExcToJS();
return pythonExcToJs();
}
x_array.call<int>("push", pythonToJs(item));
Py_DECREF(item);
@ -88,7 +89,7 @@ val pythonToJs(PyObject *x) {
}
return x_object;
} else {
return val(x);
return val(Py(x));
}
}

View File

@ -6,7 +6,7 @@
#include <emscripten/val.h>
#include <Python.h>
emscripten::val pythonExcToJS();
emscripten::val pythonExcToJs();
emscripten::val pythonToJs(PyObject *x);
int pythonToJs_Ready();

View File

@ -59,7 +59,7 @@ val runPython(std::wstring code) {
}
ret = PyRun_StringFlags(&*code_utf8.begin(), Py_file_input, globals, locals, &cf);
if (ret == NULL) {
return pythonExcToJS();
return pythonExcToJs();
}
Py_DECREF(ret);
}
@ -78,7 +78,7 @@ val runPython(std::wstring code) {
}
if (ret == NULL) {
return pythonExcToJS();
return pythonExcToJs();
}
// Now copy all the variables over to the Javascript side