2018-06-21 15:19:34 +00:00
|
|
|
# Type conversions
|
|
|
|
|
|
|
|
Python to Javascript conversions occur:
|
|
|
|
|
|
|
|
- when returning the final expression from a `pyodide.runPython` call (evaluating a Python cell in Iodide)
|
|
|
|
- using `pyodide.pyimport`
|
|
|
|
- passing arguments to a Javascript function from Python
|
|
|
|
|
|
|
|
Javascript to Python conversions occur:
|
|
|
|
|
|
|
|
- when using the `from js import ...` syntax
|
|
|
|
- returning the result of a Javascript function to Python
|
|
|
|
|
|
|
|
## Basic types
|
|
|
|
|
|
|
|
The following basic types are implicitly converted between Javascript and
|
|
|
|
Python. The values are copied and any connection to the original object is lost.
|
|
|
|
|
2018-06-22 15:49:32 +00:00
|
|
|
| Python | Javascript |
|
|
|
|
|-----------------|---------------------|
|
|
|
|
| `int`, `float` | `Number` |
|
|
|
|
| `str` | `String` |
|
|
|
|
| `True` | `true` |
|
|
|
|
| `False` | `false` |
|
|
|
|
| `None` | `undefined`, `null` |
|
|
|
|
| `list`, `tuple` | `Array` |
|
|
|
|
| `dict` | `Object` |
|
2018-06-21 15:19:34 +00:00
|
|
|
|
|
|
|
Additionally, Python `bytes` and `buffer` objects are converted to/from Javascript
|
|
|
|
`Uint8ClampedArray` typed arrays. In this case, however, the underlying data is
|
|
|
|
not copied, and is shared between the Python and Javascript sides. This makes
|
|
|
|
passing raw memory between the languages (which in practice can be quite large)
|
|
|
|
very efficient.
|
|
|
|
|
|
|
|
Aside: This is the technology on which matplotlib images are passed to
|
|
|
|
Javascript to render in a canvas, and will be the basis of sharing Numpy arrays
|
|
|
|
with n-dimensional array data structures in Javascript.
|
|
|
|
|
|
|
|
## Class instances
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
2018-06-25 20:54:08 +00:00
|
|
|
### Javascript from Python
|
2018-06-21 15:19:34 +00:00
|
|
|
|
2018-06-22 15:49:32 +00:00
|
|
|
When passing a Javascript object to Python, an extension type is used to
|
2018-06-21 15:19:34 +00:00
|
|
|
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):
|
|
|
|
|
2018-06-25 20:54:08 +00:00
|
|
|
| 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
|
2018-06-21 15:19:34 +00:00
|
|
|
|
|
|
|
When passing a Python object to Javascript, the Javascript [Proxy
|
|
|
|
API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
|
|
|
is used to delegate Javascript operations to the Python side. In general, the
|
|
|
|
Proxy API is more limited than what can be done with a Python extension, so
|
|
|
|
there are certain operations that are impossible or more cumbersome when using
|
|
|
|
Python from Javascript than vice versa. The most notable limitation is that
|
2018-06-22 15:49:32 +00:00
|
|
|
while Python has distinct ways of accessing attributes and items (`x.foo` and
|
2018-06-21 15:19:34 +00:00
|
|
|
`x[foo]`), Javascript conflates these two concepts. The following operations are
|
|
|
|
currently supported:
|
|
|
|
|
2018-06-22 15:49:32 +00:00
|
|
|
| Javascript | Python |
|
|
|
|
|----------------|--------------------------|
|
|
|
|
| `foo in x` | `hasattr(x, 'foo')` |
|
|
|
|
| `x.foo` | `getattr(x, 'foo')` |
|
|
|
|
| `x.foo = bar` | `setattr(x, 'foo', bar)` |
|
|
|
|
| `delete x.foo` | `delattr(x, 'foo')` |
|
|
|
|
| `x.ownKeys()` | `dir(x)` |
|
|
|
|
| `x(...)` | `x(...)` |
|
|
|
|
| `x.foo(...)` | `x.foo(...)` |
|
2018-06-21 15:19:34 +00:00
|
|
|
|
|
|
|
An additional limitation is that when passing a Python object to Javascript,
|
|
|
|
there is no way for Javascript to automatically garbage collect that object.
|
2018-06-24 16:29:46 +00:00
|
|
|
Therefore, custom Python objects must be manually destroyed when passed to Javascript, or
|
|
|
|
they will leak. To do this, call `.destroy()` on the object, after which Javascript will no longer have access to the object.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var foo = pyodide.pyimport('foo');
|
|
|
|
foo.call_method();
|
|
|
|
foo.destroy();
|
|
|
|
foo.call_method(); // This will raise an exception, since the object has been
|
|
|
|
// destroyed
|
|
|
|
```
|
2018-06-21 15:19:34 +00:00
|
|
|
|
2018-06-22 15:49:32 +00:00
|
|
|
## Using Python objects from Javascript
|
2018-06-21 15:19:34 +00:00
|
|
|
|
|
|
|
A Python object (in global scope) can be brought over to Javascript using the
|
|
|
|
`pyodide.pyimport` function. It takes a string giving the name of the variable,
|
|
|
|
and returns the object, converted to Javascript (See [type
|
|
|
|
conversions](type_conversions.md)).
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var sys = pyodide.pyimport('sys');
|
|
|
|
```
|
|
|
|
|
2018-06-22 15:49:32 +00:00
|
|
|
## Using Javascript objects from Python
|
2018-06-21 15:19:34 +00:00
|
|
|
|
|
|
|
Javascript objects can be accessed from Python using the `from js import ...`
|
|
|
|
syntax. The object must be in the global (`window`) namespace.
|
|
|
|
|
|
|
|
```python
|
|
|
|
from js import document
|
|
|
|
document.title = 'New window title'
|
|
|
|
```
|