mirror of https://github.com/pyodide/pyodide.git
Fix #3: Wrap Python objects for Javascript usage
This commit is contained in:
parent
64ded33c4f
commit
df1c2e2cad
2
Makefile
2
Makefile
|
@ -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))
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue