mirror of https://github.com/pyodide/pyodide.git
Add 'from js import X' on Python side
This commit is contained in:
parent
d4eb51b167
commit
314ec07227
2
Makefile
2
Makefile
|
@ -22,7 +22,7 @@ LDFLAGS=$(OPTFLAGS) \
|
|||
all: build/pyodide.asm.html build/pyodide.js
|
||||
|
||||
|
||||
build/pyodide.asm.html: src/main.bc src/jsproxy.bc src/js2python.bc src/pylocals.bc \
|
||||
build/pyodide.asm.html: src/main.bc src/jsimport.bc src/jsproxy.bc src/js2python.bc \
|
||||
src/pyproxy.bc src/python2js.bc src/runpython.bc root
|
||||
$(CC) -s EXPORT_NAME="'pyodide'" --bind -o $@ $(filter %.bc,$^) $(LDFLAGS) \
|
||||
$(foreach d,$(wildcard root/*),--preload-file $d@/$(notdir $d))
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#include "jsimport.hpp"
|
||||
|
||||
#include "js2python.hpp"
|
||||
|
||||
using emscripten::val;
|
||||
|
||||
static PyObject *original__import__;
|
||||
PyObject *globals = NULL;
|
||||
PyObject *original_globals = NULL;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
} JsImport;
|
||||
|
||||
static PyObject *JsImport_Call(PyObject *self, PyObject *args, PyObject *kwargs) {
|
||||
PyObject *name = PyTuple_GET_ITEM(args, 0);
|
||||
if (PyUnicode_CompareWithASCIIString(name, "js") == 0) {
|
||||
PyObject *locals = PyTuple_GET_ITEM(args, 2);
|
||||
PyObject *fromlist = PyTuple_GET_ITEM(args, 3);
|
||||
Py_ssize_t n = PySequence_Size(fromlist);
|
||||
PyObject *jsmod = PyModule_New("js");
|
||||
PyObject *d = PyModule_GetDict(jsmod);
|
||||
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||
PyObject *key = PySequence_GetItem(fromlist, i);
|
||||
if (key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *c = PyUnicode_AsUTF8(key);
|
||||
if (c == NULL) {
|
||||
Py_DECREF(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val jsval = val::global(c);
|
||||
PyObject *pyval = jsToPython(jsval);
|
||||
if (PyDict_SetItem(d, key, pyval)) {
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(pyval);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(pyval);
|
||||
}
|
||||
|
||||
return jsmod;
|
||||
} else {
|
||||
return PyObject_Call(original__import__, args, kwargs);
|
||||
}
|
||||
}
|
||||
|
||||
static PyTypeObject JsImportType = {
|
||||
.tp_name = "JsImport",
|
||||
.tp_basicsize = sizeof(JsImport),
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_call = JsImport_Call,
|
||||
.tp_doc = "An import hook that imports things from Javascript."
|
||||
};
|
||||
|
||||
static PyObject *JsImport_New() {
|
||||
JsImport *self;
|
||||
self = (JsImport *)JsImportType.tp_alloc(&JsImportType, 0);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
int JsImport_Ready() {
|
||||
if (PyType_Ready(&JsImportType)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PyObject *m = PyImport_AddModule("builtins");
|
||||
if (m == NULL) {
|
||||
return 1;
|
||||
}
|
||||
PyObject *d = PyModule_GetDict(m);
|
||||
if (d == NULL) {
|
||||
return 1;
|
||||
}
|
||||
original__import__ = PyDict_GetItemString(d, "__import__");
|
||||
if (original__import__ == NULL) {
|
||||
return 1;
|
||||
}
|
||||
Py_INCREF(original__import__);
|
||||
|
||||
PyObject *importer = JsImport_New();
|
||||
if (importer == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (PyDict_SetItemString(d, "__import__", importer)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
m = PyImport_AddModule("__main__");
|
||||
if (m == NULL)
|
||||
return 1;
|
||||
globals = PyModule_GetDict(m);
|
||||
|
||||
m = PyImport_AddModule("builtins");
|
||||
PyDict_Update(globals, PyModule_GetDict(m));
|
||||
|
||||
original_globals = PyDict_Copy(globals);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef JSIMPORT_H
|
||||
#define JSIMPORT_H
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
int JsImport_Ready();
|
||||
extern PyObject *globals;
|
||||
extern PyObject *original_globals;
|
||||
|
||||
#endif /* JSIMPORT_H */
|
|
@ -4,9 +4,9 @@
|
|||
#include <Python.h>
|
||||
#include <node.h> // from CPython
|
||||
|
||||
#include "jsimport.hpp"
|
||||
#include "jsproxy.hpp"
|
||||
#include "js2python.hpp"
|
||||
#include "pylocals.hpp"
|
||||
#include "pyproxy.hpp"
|
||||
#include "python2js.hpp"
|
||||
#include "runpython.hpp"
|
||||
|
@ -39,7 +39,7 @@ extern "C" {
|
|||
if (JsProxy_Ready() ||
|
||||
jsToPython_Ready() ||
|
||||
pythonToJs_Ready() ||
|
||||
PyLocals_Ready()) {
|
||||
JsImport_Ready()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
123
src/pylocals.cpp
123
src/pylocals.cpp
|
@ -1,123 +0,0 @@
|
|||
#include "pylocals.hpp"
|
||||
#include "js2python.hpp"
|
||||
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
using emscripten::val;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// PyLocals
|
||||
//
|
||||
// This is an object designed to be used as a "locals" namespace dictionary.
|
||||
// It first looks for things in its own internal dictionary, and failing that,
|
||||
// looks in the Javascript global namespace. This is a way of merging the
|
||||
// Python and Javascript namespaces without fullying copying either one.
|
||||
|
||||
PyObject *locals = NULL;
|
||||
PyObject *globals = NULL;
|
||||
PyObject *original_globals = NULL;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *locals;
|
||||
} PyLocals;
|
||||
|
||||
static void PyLocals_dealloc(PyLocals *self) {
|
||||
Py_DECREF(self->locals);
|
||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||
}
|
||||
|
||||
static Py_ssize_t PyLocals_length(PyObject *o) {
|
||||
PyLocals *self = (PyLocals *)o;
|
||||
|
||||
return PyDict_Size(self->locals);
|
||||
}
|
||||
|
||||
PyObject* PyLocals_get(PyObject *o, PyObject *key) {
|
||||
PyLocals *self = (PyLocals *)o;
|
||||
|
||||
{
|
||||
PyObject *str = PyObject_Str(key);
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *c = PyUnicode_AsUTF8(str);
|
||||
Py_DECREF(str);
|
||||
}
|
||||
|
||||
PyObject *py_val = PyDict_GetItem(self->locals, key);
|
||||
if (py_val != NULL) {
|
||||
Py_INCREF(py_val);
|
||||
return py_val;
|
||||
}
|
||||
|
||||
PyObject *str = PyObject_Str(key);
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *c = PyUnicode_AsUTF8(str);
|
||||
val v = val::global(c);
|
||||
Py_DECREF(str);
|
||||
return jsToPython(v);
|
||||
}
|
||||
|
||||
int PyLocals_set(PyObject *o, PyObject *k, PyObject *v) {
|
||||
PyLocals *self = (PyLocals *)o;
|
||||
|
||||
if (v == NULL) {
|
||||
// TODO: This might not actually be here to delete...
|
||||
return PyDict_DelItem(self->locals, k);
|
||||
} else {
|
||||
return PyDict_SetItem(self->locals, k, v);
|
||||
}
|
||||
}
|
||||
|
||||
static PyMappingMethods PyLocals_as_mapping = {
|
||||
PyLocals_length,
|
||||
PyLocals_get,
|
||||
PyLocals_set
|
||||
};
|
||||
|
||||
static PyTypeObject PyLocalsType = {
|
||||
.tp_name = "PyLocals",
|
||||
.tp_basicsize = sizeof(PyLocals),
|
||||
.tp_dealloc = (destructor)PyLocals_dealloc,
|
||||
.tp_as_mapping = &PyLocals_as_mapping,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "A proxy that looks in a dict first, otherwise in the global JS namespace"
|
||||
};
|
||||
|
||||
static PyObject *PyLocals_cnew(PyObject *d)
|
||||
{
|
||||
PyLocals *self;
|
||||
self = (PyLocals *)PyLocalsType.tp_alloc(&PyLocalsType, 0);
|
||||
if (self != NULL) {
|
||||
Py_INCREF(d);
|
||||
self->locals = d;
|
||||
}
|
||||
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
int PyLocals_Ready() {
|
||||
if (PyType_Ready(&PyLocalsType) < 0)
|
||||
return 1;
|
||||
|
||||
PyObject *m = PyImport_AddModule("__main__");
|
||||
if (m == NULL)
|
||||
return 1;
|
||||
globals = PyModule_GetDict(m);
|
||||
|
||||
m = PyImport_AddModule("builtins");
|
||||
PyDict_Update(globals, PyModule_GetDict(m));
|
||||
|
||||
original_globals = PyDict_Copy(globals);
|
||||
|
||||
locals = PyLocals_cnew(globals);
|
||||
if (locals == NULL)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef PYLOCALS_H
|
||||
#define PYLOCALS_H
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
int PyLocals_Ready();
|
||||
|
||||
extern PyObject *locals;
|
||||
extern PyObject *globals;
|
||||
extern PyObject *original_globals;
|
||||
|
||||
#endif /* PYLOCALS_H */
|
|
@ -6,7 +6,7 @@
|
|||
#include <Python.h>
|
||||
#include <node.h> // from Python
|
||||
|
||||
#include "pylocals.hpp"
|
||||
#include "jsimport.hpp"
|
||||
#include "python2js.hpp"
|
||||
|
||||
using emscripten::val;
|
||||
|
@ -57,7 +57,7 @@ val runPython(std::wstring code) {
|
|||
*last_line = 0;
|
||||
last_line++;
|
||||
}
|
||||
ret = PyRun_StringFlags(&*code_utf8.begin(), Py_file_input, globals, locals, &cf);
|
||||
ret = PyRun_StringFlags(&*code_utf8.begin(), Py_file_input, globals, globals, &cf);
|
||||
if (ret == NULL) {
|
||||
return pythonExcToJs();
|
||||
}
|
||||
|
@ -70,10 +70,10 @@ val runPython(std::wstring code) {
|
|||
ret = Py_None;
|
||||
break;
|
||||
case 1:
|
||||
ret = PyRun_StringFlags(&*last_line, Py_eval_input, globals, locals, &cf);
|
||||
ret = PyRun_StringFlags(&*last_line, Py_eval_input, globals, globals, &cf);
|
||||
break;
|
||||
case 2:
|
||||
ret = PyRun_StringFlags(&*last_line, Py_file_input, globals, locals, &cf);
|
||||
ret = PyRun_StringFlags(&*last_line, Py_file_input, globals, globals, &cf);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -81,19 +81,6 @@ val runPython(std::wstring code) {
|
|||
return pythonExcToJs();
|
||||
}
|
||||
|
||||
// Now copy all the variables over to the Javascript side
|
||||
{
|
||||
val js_globals = val::global("window");
|
||||
PyObject *k, *v;
|
||||
Py_ssize_t pos = 0;
|
||||
|
||||
while (PyDict_Next(globals, &pos, &k, &v)) {
|
||||
if (!PyDict_Contains(original_globals, k)) {
|
||||
js_globals.set(pythonToJs(k), pythonToJs(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = pythonToJs(ret);
|
||||
Py_DECREF(ret);
|
||||
return result;
|
||||
|
|
Loading…
Reference in New Issue