Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pyodide
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
pyodide
Commits
e93cc2b1
Commit
e93cc2b1
authored
Jun 26, 2018
by
Michael Droettboom
Committed by
GitHub
Jun 26, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #61 from iodide-project/more-proxy
Add a lot more jsproxy functionality
parents
4331456f
6b8f5e27
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
350 additions
and
32 deletions
+350
-32
docs/type_conversions.md
docs/type_conversions.md
+43
-11
src/hiwire.c
src/hiwire.c
+55
-0
src/hiwire.h
src/hiwire.h
+88
-1
src/jsproxy.c
src/jsproxy.c
+113
-20
test/test_python.py
test/test_python.py
+51
-0
No files found.
docs/type_conversions.md
View file @
e93cc2b1
...
...
@@ -42,23 +42,55 @@ Any of the types not listed above are shared between languages using proxies
that allow methods and some operators to be called on the object from the other
language.
### Javascript from Python
When passing a Javascript object to Python, an extension type is used to
delegate Python operations to the Javascript side. The following operations are
currently supported. (More should be possible in the future -- work in ongoing
to make this more complete):
| Python | Javascript |
|----------------|----------------|
|
`repr(x)`
|
`x.toString()`
|
|
`x.foo`
|
`x.foo`
|
|
`x.foo = bar`
|
`x.foo = bar`
|
|
`x(...)`
|
`x(...)`
|
|
`x.foo(...)`
|
`x.foo(...)`
|
|
`X.new(...)`
|
`new X(...)`
|
|
`len(x)`
|
`x.length`
|
|
`x[foo]`
|
`x[foo]`
|
|
`x[foo] = bar`
|
`x[foo] = bar`
|
| Python | Javascript |
|----------------|-----------------|
|
`repr(x)`
|
`x.toString()`
|
|
`x.foo`
|
`x.foo`
|
|
`x.foo = bar`
|
`x.foo = bar`
|
|
`del x.foo`
|
`delete x.foo`
|
|
`x(...)`
|
`x(...)`
|
|
`x.foo(...)`
|
`x.foo(...)`
|
|
`X.new(...)`
|
`new X(...)`
|
|
`len(x)`
|
`x.length`
|
|
`x[foo]`
|
`x[foo]`
|
|
`x[foo] = bar`
|
`x[foo] = bar`
|
|
`del x[foo]`
|
`delete x[foo]`
|
|
`x == y`
|
`x == y`
|
|
`x.typeof`
|
`typeof x`
|
One important difference between Python objects and Javascript objects is that
if you access a missing member in Python, an exception is raised. In Javascript,
it returns
`undefined`
. Since we can't make any assumptions about whether the
Javascript member is missing or simply set to
`undefined`
, Python mirrors the
Javascript behavior. For example:
```
javascript
// Javascript
class
Point
{
constructor
(
x
,
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
}
point
=
new
Point
(
42
,
43
))
```
```
python
# python
from
js
import
point
assert
point
.
y
==
43
del
point
.
y
assert
point
.
y
is
None
```
### Python from Javascript
When passing a Python object to Javascript, the Javascript
[
Proxy
API
](
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
)
...
...
src/hiwire.c
View file @
e93cc2b1
...
...
@@ -100,6 +100,12 @@ EM_JS(void, hiwire_set_member_string, (int idobj, int ptrkey, int idval), {
jsobj
[
jskey
]
=
jsval
;
});
EM_JS
(
void
,
hiwire_delete_member_string
,
(
int
idobj
,
int
ptrkey
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
var
jskey
=
UTF8ToString
(
ptrkey
);
delete
jsobj
[
jskey
];
});
EM_JS
(
int
,
hiwire_get_member_int
,
(
int
idobj
,
int
idx
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
return
Module
.
hiwire_new_value
(
jsobj
[
idx
]);
...
...
@@ -109,6 +115,25 @@ EM_JS(void, hiwire_set_member_int, (int idobj, int idx, int idval), {
Module
.
hiwire_get_value
(
idobj
)[
idx
]
=
Module
.
hiwire_get_value
(
idval
);
});
EM_JS
(
int
,
hiwire_get_member_obj
,
(
int
idobj
,
int
ididx
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
var
jsidx
=
Module
.
hiwire_get_value
(
ididx
);
return
Module
.
hiwire_new_value
(
jsobj
[
jsidx
]);
});
EM_JS
(
void
,
hiwire_set_member_obj
,
(
int
idobj
,
int
ididx
,
int
idval
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
var
jsidx
=
Module
.
hiwire_get_value
(
ididx
);
var
jsval
=
Module
.
hiwire_get_value
(
idval
);
jsobj
[
jsidx
]
=
jsval
;
});
EM_JS
(
void
,
hiwire_delete_member_obj
,
(
int
idobj
,
int
ididx
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
var
jsidx
=
Module
.
hiwire_get_value
(
ididx
);
delete
jsobj
[
jsidx
];
});
EM_JS
(
void
,
hiwire_call
,
(
int
idfunc
,
int
idargs
),
{
var
jsfunc
=
Module
.
hiwire_get_value
(
idfunc
);
var
jsargs
=
Module
.
hiwire_get_value
(
idargs
);
...
...
@@ -146,3 +171,33 @@ EM_JS(int, hiwire_is_function, (int idobj), {
EM_JS
(
int
,
hiwire_to_string
,
(
int
idobj
),
{
return
Module
.
hiwire_new_value
(
Module
.
hiwire_get_value
(
idobj
).
toString
());
});
EM_JS
(
int
,
hiwire_typeof
,
(
int
idobj
),
{
return
Module
.
hiwire_new_value
(
typeof
Module
.
hiwire_get_value
(
idobj
));
});
#define MAKE_OPERATOR(name, op) \
EM_JS(int, hiwire_##name, (int ida, int idb), { \
return (Module.hiwire_get_value(ida) op Module.hiwire_get_value(idb)) ? 1 : 0; \
});
MAKE_OPERATOR
(
less_than
,
<
);
MAKE_OPERATOR
(
less_than_equal
,
<=
);
MAKE_OPERATOR
(
equal
,
==
);
MAKE_OPERATOR
(
not_equal
,
!=
);
MAKE_OPERATOR
(
greater_than
,
>
);
MAKE_OPERATOR
(
greater_than_equal
,
>=
);
EM_JS
(
int
,
hiwire_next
,
(
int
idobj
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
if
(
jsobj
.
next
===
undefined
)
{
return
-
1
;
}
return
Module
.
hiwire_new_value
(
jsobj
.
next
());
});
EM_JS
(
int
,
hiwire_nonzero
,
(
int
idobj
),
{
var
jsobj
=
Module
.
hiwire_get_value
(
idobj
);
return
(
jsobj
!=
0
)
?
1
:
0
;
});
src/hiwire.h
View file @
e93cc2b1
...
...
@@ -179,6 +179,14 @@ hiwire_get_member_string(int idobj, int ptrname);
void
hiwire_set_member_string
(
int
idobj
,
int
ptrname
,
int
idval
);
/**
* Delete an object member by string.
*
* The string is a char* to null-terminated UTF8.
*/
void
hiwire_delete_member_string
(
int
idobj
,
int
ptrname
);
/**
* Get an object member by integer.
*
...
...
@@ -194,11 +202,32 @@ hiwire_get_member_int(int idobj, int idx);
*
* The integer is a C integer, not an id reference to a Javascript integer.
*
* Returns: New reference
*/
void
hiwire_set_member_int
(
int
idobj
,
int
idx
,
int
idval
);
/**
* Get an object member by object.
*
* Returns: New reference
*/
int
hiwire_get_member_obj
(
int
idobj
,
int
ididx
);
/**
* Set an object member by object.
*
*/
void
hiwire_set_member_obj
(
int
idobj
,
int
ididx
,
int
idval
);
/**
* Delete an object member by object.
*
*/
void
hiwire_delete_member_obj
(
int
idobj
,
int
ididx
);
/**
* Call a function
*
...
...
@@ -255,4 +284,62 @@ hiwire_is_function(int idobj);
int
hiwire_to_string
(
int
idobj
);
/**
* Gets the "typeof" string for a value.
*
* Returns: New reference to Javascript string
*/
int
hiwire_typeof
(
int
idobj
);
/**
* Returns non-zero if a < b.
*/
int
hiwire_less_than
(
int
ida
,
int
idb
);
/**
* Returns non-zero if a <= b.
*/
int
hiwire_less_than_equal
(
int
ida
,
int
idb
);
/**
* Returns non-zero if a == b.
*/
int
hiwire_equal
(
int
ida
,
int
idb
);
/**
* Returns non-zero if a != b.
*/
int
hiwire_not_equal
(
int
idx
,
int
idb
);
/**
* Returns non-zero if a > b.
*/
int
hiwire_greater_than
(
int
ida
,
int
idb
);
/**
* Returns non-zero if a >= b.
*/
int
hiwire_greater_than_equal
(
int
ida
,
int
idb
);
/**
* Calls the `next` function on an iterator.
*
* Returns: -1 if `next` function is undefined.
*/
int
hiwire_next
(
int
idobj
);
/**
* Returns 1 if the value is non-zero.
*
*/
int
hiwire_nonzero
(
int
idobj
);
#endif
/* HIWIRE_H */
src/jsproxy.c
View file @
e93cc2b1
...
...
@@ -49,6 +49,12 @@ JsProxy_GetAttr(PyObject* o, PyObject* attr_name)
if
(
strncmp
(
key
,
"new"
,
4
)
==
0
)
{
Py_DECREF
(
str
);
return
PyObject_GenericGetAttr
(
o
,
attr_name
);
}
else
if
(
strncmp
(
key
,
"typeof"
,
7
)
==
0
)
{
Py_DECREF
(
str
);
int
idval
=
hiwire_typeof
(
self
->
js
);
PyObject
*
result
=
js2python
(
idval
);
hiwire_decref
(
idval
);
return
result
;
}
int
idresult
=
hiwire_get_member_string
(
self
->
js
,
(
int
)
key
);
...
...
@@ -74,9 +80,14 @@ JsProxy_SetAttr(PyObject* o, PyObject* attr_name, PyObject* pyvalue)
return
-
1
;
}
char
*
key
=
PyUnicode_AsUTF8
(
attr_name_py_str
);
int
idvalue
=
python2js
(
pyvalue
);
hiwire_set_member_string
(
self
->
js
,
(
int
)
key
,
idvalue
);
hiwire_decref
(
idvalue
);
if
(
pyvalue
==
NULL
)
{
hiwire_delete_member_string
(
self
->
js
,
(
int
)
key
);
}
else
{
int
idvalue
=
python2js
(
pyvalue
);
hiwire_set_member_string
(
self
->
js
,
(
int
)
key
,
idvalue
);
hiwire_decref
(
idvalue
);
}
Py_DECREF
(
attr_name_py_str
);
return
0
;
...
...
@@ -104,6 +115,84 @@ JsProxy_Call(PyObject* o, PyObject* args, PyObject* kwargs)
return
pyresult
;
}
static
PyObject
*
JsProxy_RichCompare
(
PyObject
*
a
,
PyObject
*
b
,
int
op
)
{
JsProxy
*
aproxy
=
(
JsProxy
*
)
a
;
if
(
!
JsProxy_Check
(
b
))
{
switch
(
op
)
{
case
Py_EQ
:
Py_RETURN_FALSE
;
case
Py_NE
:
Py_RETURN_TRUE
;
default:
return
Py_NotImplemented
;
}
}
int
result
;
int
ida
=
python2js
(
a
);
int
idb
=
python2js
(
b
);
switch
(
op
)
{
case
Py_LT
:
result
=
hiwire_less_than
(
ida
,
idb
);
break
;
case
Py_LE
:
result
=
hiwire_less_than_equal
(
ida
,
idb
);
break
;
case
Py_EQ
:
result
=
hiwire_equal
(
ida
,
idb
);
break
;
case
Py_NE
:
result
=
hiwire_not_equal
(
ida
,
idb
);
break
;
case
Py_GT
:
result
=
hiwire_greater_than
(
ida
,
idb
);
break
;
case
Py_GE
:
result
=
hiwire_greater_than_equal
(
ida
,
idb
);
break
;
}
hiwire_decref
(
ida
);
hiwire_decref
(
idb
);
if
(
result
)
{
Py_RETURN_TRUE
;
}
else
{
Py_RETURN_FALSE
;
}
}
static
PyObject
*
JsProxy_GetIter
(
PyObject
*
o
)
{
Py_INCREF
(
o
);
return
o
;
}
static
PyObject
*
JsProxy_IterNext
(
PyObject
*
o
)
{
JsProxy
*
self
=
(
JsProxy
*
)
o
;
int
idresult
=
hiwire_next
(
self
->
js
);
if
(
idresult
==
-
1
)
{
return
NULL
;
}
int
iddone
=
hiwire_get_member_string
(
idresult
,
(
int
)
"done"
);
int
done
=
hiwire_nonzero
(
iddone
);
hiwire_decref
(
iddone
);
if
(
done
)
{
return
NULL
;
}
int
idvalue
=
hiwire_get_member_string
(
idresult
,
(
int
)
"value"
);
PyObject
*
pyvalue
=
js2python
(
idvalue
);
hiwire_decref
(
idvalue
);
return
pyvalue
;
}
static
PyObject
*
JsProxy_New
(
PyObject
*
o
,
PyObject
*
args
,
PyObject
*
kwargs
)
{
...
...
@@ -135,38 +224,39 @@ JsProxy_length(PyObject* o)
}
PyObject
*
JsProxy_
item
(
PyObject
*
o
,
Py_ssize_t
idx
)
JsProxy_
subscript
(
PyObject
*
o
,
PyObject
*
py
idx
)
{
JsProxy
*
self
=
(
JsProxy
*
)
o
;
int
idresult
=
hiwire_get_member_int
(
self
->
js
,
idx
);
int
ididx
=
python2js
(
pyidx
);
int
idresult
=
hiwire_get_member_obj
(
self
->
js
,
ididx
);
hiwire_decref
(
ididx
);
PyObject
*
pyresult
=
js2python
(
idresult
);
hiwire_decref
(
idresult
);
return
pyresult
;
}
int
JsProxy_ass_
item
(
PyObject
*
o
,
Py_ssize_t
idx
,
PyObject
*
value
)
JsProxy_ass_
subscript
(
PyObject
*
o
,
PyObject
*
pyidx
,
PyObject
*
py
value
)
{
JsProxy
*
self
=
(
JsProxy
*
)
o
;
int
idvalue
=
python2js
(
value
);
hiwire_set_member_int
(
self
->
js
,
idx
,
idvalue
);
hiwire_decref
(
idvalue
);
int
ididx
=
python2js
(
pyidx
);
if
(
pyvalue
==
NULL
)
{
hiwire_delete_member_obj
(
self
->
js
,
ididx
);
}
else
{
int
idvalue
=
python2js
(
pyvalue
);
hiwire_set_member_obj
(
self
->
js
,
ididx
,
idvalue
);
hiwire_decref
(
idvalue
);
}
hiwire_decref
(
ididx
);
return
0
;
}
// clang-format off
static
Py
SequenceMethods
JsProxy_Sequence
Methods
=
{
static
Py
MappingMethods
JsProxy_Mapping
Methods
=
{
JsProxy_length
,
NULL
,
NULL
,
JsProxy_item
,
NULL
,
JsProxy_ass_item
,
NULL
,
NULL
,
NULL
,
NULL
JsProxy_subscript
,
JsProxy_ass_subscript
,
};
// clang-format on
...
...
@@ -183,10 +273,13 @@ static PyTypeObject JsProxyType = {
.
tp_call
=
JsProxy_Call
,
.
tp_getattro
=
JsProxy_GetAttr
,
.
tp_setattro
=
JsProxy_SetAttr
,
.
tp_richcompare
=
JsProxy_RichCompare
,
.
tp_flags
=
Py_TPFLAGS_DEFAULT
,
.
tp_doc
=
"A proxy to make a Javascript object behave like a Python object"
,
.
tp_methods
=
JsProxy_Methods
,
.
tp_as_sequence
=
&
JsProxy_SequenceMethods
,
.
tp_as_mapping
=
&
JsProxy_MappingMethods
,
.
tp_iter
=
JsProxy_GetIter
,
.
tp_iternext
=
JsProxy_IterNext
,
.
tp_repr
=
JsProxy_Repr
};
...
...
test/test_python.py
View file @
e93cc2b1
...
...
@@ -172,6 +172,57 @@ def test_jsproxy(selenium):
assert
selenium
.
run
(
"from js import ImageData
\
n
"
"ImageData.new(64, 64)"
)
assert
selenium
.
run
(
"from js import ImageData
\
n
"
"ImageData.typeof"
)
==
'function'
selenium
.
run_js
(
"class Point {
\
n
"
" constructor(x, y) {
\
n
"
" this.x = x;
\
n
"
" this.y = y;
\
n
"
" }
\
n
"
"}
\
n
"
"window.TEST = new Point(42, 43);"
)
assert
selenium
.
run
(
"from js import TEST
\
n
"
"del TEST.y
\
n
"
"TEST.y
\
n
"
)
is
None
selenium
.
run_js
(
"class Point {
\
n
"
" constructor(x, y) {
\
n
"
" this.x = x;
\
n
"
" this.y = y;
\
n
"
" }
\
n
"
"}
\
n
"
"window.TEST = new Point(42, 43);"
)
assert
selenium
.
run
(
"from js import TEST
\
n
"
"del TEST['y']
\
n
"
"TEST['y']
\
n
"
)
is
None
assert
selenium
.
run
(
"from js import TEST
\
n
"
"TEST == TEST
\
n
"
)
assert
selenium
.
run
(
"from js import TEST
\
n
"
"TEST != 'foo'
\
n
"
)
def
test_jsproxy_iter
(
selenium
):
selenium
.
run_js
(
"function makeIterator(array) {
\
n
"
" var nextIndex = 0;
\
n
"
" return {
\
n
"
" next: function() {
\
n
"
" return nextIndex < array.length ?
\
n
"
" {value: array[nextIndex++], done: false} :
\
n
"
" {done: true};
\
n
"
" }
\
n
"
" };
\
n
"
"}
\
n
"
"window.ITER = makeIterator([1, 2, 3]);"
)
assert
selenium
.
run
(
"from js import ITER
\
n
"
"list(ITER)"
)
==
[
1
,
2
,
3
]
def
test_open_url
(
selenium
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment