2020-07-14 09:29:58 +00:00
|
|
|
# Frequently Asked Questions (FAQ)
|
|
|
|
|
2021-03-03 08:11:50 +00:00
|
|
|
## How can I load external Python files in Pyodide?
|
2020-07-14 09:29:58 +00:00
|
|
|
|
|
|
|
The two possible solutions are,
|
|
|
|
|
2021-03-03 08:11:50 +00:00
|
|
|
- include these files in a Python package, build a pure Python wheel with
|
2020-10-30 20:09:25 +00:00
|
|
|
`python setup.py bdist_wheel` and
|
|
|
|
{ref}`load it with micropip <micropip-installing-from-arbitrary-urls>`.
|
2021-03-03 08:11:50 +00:00
|
|
|
- fetch the Python code as a string and evaluate it in Python,
|
2020-07-14 09:29:58 +00:00
|
|
|
```js
|
2020-12-24 17:14:49 +00:00
|
|
|
pyodide.runPython(await fetch('https://some_url/...'))
|
2020-07-14 09:29:58 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
In both cases, files need to be served with a web server and cannot be loaded from local file system.
|
|
|
|
|
2020-10-30 20:09:25 +00:00
|
|
|
## Why can't I load files from the local file system?
|
2020-07-14 09:29:58 +00:00
|
|
|
|
2021-03-20 18:56:10 +00:00
|
|
|
For security reasons Javascript in the browser is not allowed to load local data
|
|
|
|
files. You need to serve them with a web-browser. Recently there is a
|
|
|
|
[Native File System API](https://wicg.github.io/file-system-access/) supported in Chrome
|
|
|
|
but not in Firefox.
|
|
|
|
[There is a discussion about implementing it for Firefox here.](https://github.com/mozilla/standards-positions/issues/154)
|
2020-12-24 17:14:49 +00:00
|
|
|
|
|
|
|
|
2021-03-20 18:56:10 +00:00
|
|
|
## How can I change the behavior of {any}`runPython <pyodide.runPython>` and {any}`runPythonAsync <pyodide.runPythonAsync>`?
|
2021-04-06 08:14:07 +00:00
|
|
|
You can directly call Python functions from Javascript. For many purposes it
|
|
|
|
makes sense to make your own Python function as an entrypoint and call that
|
|
|
|
instead of using `runPython`. The definitions of {any}`runPython
|
|
|
|
<pyodide.runPython>` and {any}`runPythonAsync <pyodide.runPythonAsync>` are very
|
|
|
|
simple:
|
2021-01-02 22:44:24 +00:00
|
|
|
```javascript
|
|
|
|
function runPython(code){
|
|
|
|
pyodide.pyodide_py.eval_code(code, pyodide.globals);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
async function runPythonAsync(code, messageCallback, errorCallback) {
|
|
|
|
await pyodide.loadPackagesFromImports(code, messageCallback, errorCallback);
|
2021-04-06 22:11:00 +00:00
|
|
|
let coroutine = pyodide.pyodide_py.eval_code_async(code, pyodide.globals);
|
|
|
|
try {
|
|
|
|
let result = await coroutine;
|
|
|
|
return result;
|
|
|
|
} finally {
|
|
|
|
coroutine.destroy();
|
|
|
|
}
|
2021-01-02 22:44:24 +00:00
|
|
|
};
|
|
|
|
```
|
2021-04-06 22:11:00 +00:00
|
|
|
To make your own version of {any}`runPython <pyodide.runPython>` you could do:
|
2021-02-15 08:38:49 +00:00
|
|
|
```pyodide
|
|
|
|
pyodide.runPython(`
|
2021-01-02 22:44:24 +00:00
|
|
|
import pyodide
|
|
|
|
def my_eval_code(code, ns):
|
|
|
|
extra_info = None
|
2021-03-20 18:56:10 +00:00
|
|
|
result = pyodide.eval_code(code, ns)
|
|
|
|
return ns["extra_info"], result]
|
2021-02-15 08:38:49 +00:00
|
|
|
`)
|
2021-01-02 22:44:24 +00:00
|
|
|
|
|
|
|
function myRunPython(code){
|
2021-02-23 19:25:10 +00:00
|
|
|
return pyodide.globals.get("my_eval_code")(code, pyodide.globals);
|
2021-01-02 22:44:24 +00:00
|
|
|
}
|
|
|
|
```
|
2021-04-06 22:11:00 +00:00
|
|
|
|
2021-01-02 22:44:24 +00:00
|
|
|
Then `pyodide.myRunPython("2+7")` returns `[None, 9]` and
|
|
|
|
`pyodide.myRunPython("extra_info='hello' ; 2 + 2")` returns `['hello', 4]`.
|
2021-03-20 18:56:10 +00:00
|
|
|
If you want to change which packages {any}`pyodide.loadPackagesFromImports` loads, you can
|
|
|
|
monkey patch {any}`pyodide.find_imports` which takes `code` as an argument
|
2021-01-02 22:44:24 +00:00
|
|
|
and returns a list of packages imported.
|
|
|
|
|
2021-01-17 04:33:50 +00:00
|
|
|
## How can I execute code in a custom namespace?
|
2020-12-24 17:14:49 +00:00
|
|
|
|
2021-02-15 19:38:07 +00:00
|
|
|
The second argument to {any}`pyodide.eval_code` is a global namespace to execute the code in.
|
2021-03-20 18:56:10 +00:00
|
|
|
The namespace is a Python dictionary.
|
2021-02-15 19:38:07 +00:00
|
|
|
```javascript
|
|
|
|
let my_namespace = pyodide.globals.dict();
|
2021-04-06 22:11:00 +00:00
|
|
|
pyodide.runPython(`x = 1 + 1`, my_namespace);
|
|
|
|
pyodide.runPython(`y = x ** x`, my_namespace);
|
2021-02-15 19:38:07 +00:00
|
|
|
my_namespace.y; // ==> 4
|
|
|
|
```
|
2020-11-07 11:21:54 +00:00
|
|
|
|
|
|
|
## How to detect that code is run with Pyodide?
|
|
|
|
|
|
|
|
**At run time**, you can detect that a code is running with Pyodide using,
|
|
|
|
```py
|
|
|
|
import sys
|
|
|
|
|
|
|
|
if "pyodide" in sys.modules:
|
|
|
|
# running in Pyodide
|
|
|
|
```
|
|
|
|
|
|
|
|
More generally you can detect Python built with Emscripten (which includes
|
|
|
|
Pyodide) with,
|
|
|
|
```py
|
|
|
|
import platform
|
|
|
|
|
|
|
|
if platform.system() == 'Emscripten':
|
|
|
|
# running in Pyodide or other Emscripten based build
|
|
|
|
```
|
|
|
|
|
|
|
|
This however will not work at build time (i.e. in a `setup.py`) due to the way
|
2021-03-03 08:11:50 +00:00
|
|
|
the Pyodide build system works. It first compiles packages with the host compiler
|
2020-11-07 11:21:54 +00:00
|
|
|
(e.g. gcc) and then re-runs the compilation commands with emsdk. So the `setup.py` is
|
2021-04-12 16:34:02 +00:00
|
|
|
never run inside the Pyodide environment.
|
2020-11-07 11:21:54 +00:00
|
|
|
|
2021-03-03 08:11:50 +00:00
|
|
|
To detect Pyodide, **at build time** use,
|
2020-11-07 11:21:54 +00:00
|
|
|
```python
|
|
|
|
import os
|
|
|
|
|
2021-01-01 07:48:28 +00:00
|
|
|
if "PYODIDE" in os.environ:
|
2020-11-07 11:21:54 +00:00
|
|
|
# building for Pyodide
|
|
|
|
```
|
2021-01-01 07:48:28 +00:00
|
|
|
We used to use the environment variable `PYODIDE_BASE_URL` for this purpose,
|
|
|
|
but this usage is deprecated.
|
2021-01-17 04:33:50 +00:00
|
|
|
|
|
|
|
|
2021-03-03 08:11:50 +00:00
|
|
|
## How do I create custom Python packages from Javascript?
|
2021-01-17 04:33:50 +00:00
|
|
|
|
2021-03-20 18:56:10 +00:00
|
|
|
Put a collection of functions into a Javascript object and use {any}`pyodide.registerJsModule`:
|
2021-01-17 04:33:50 +00:00
|
|
|
Javascript:
|
|
|
|
```javascript
|
|
|
|
let my_module = {
|
|
|
|
f : function(x){
|
|
|
|
return x*x + 1;
|
|
|
|
},
|
|
|
|
g : function(x){
|
|
|
|
console.log(`Calling g on argument ${x}`);
|
|
|
|
return x;
|
|
|
|
},
|
|
|
|
submodule : {
|
|
|
|
h : function(x) {
|
|
|
|
return x*x - 1;
|
|
|
|
},
|
|
|
|
c : 2,
|
2021-03-20 18:56:10 +00:00
|
|
|
},
|
2021-01-17 04:33:50 +00:00
|
|
|
};
|
|
|
|
pyodide.registerJsModule("my_js_module", my_module);
|
|
|
|
```
|
|
|
|
You can import your package like a normal Python package:
|
2021-02-15 08:38:49 +00:00
|
|
|
```py
|
2021-01-17 04:33:50 +00:00
|
|
|
import my_js_module
|
|
|
|
from my_js_module.submodule import h, c
|
|
|
|
assert my_js_module.f(7) == 50
|
|
|
|
assert h(9) == 80
|
|
|
|
assert c == 2
|
2021-04-06 08:14:07 +00:00
|
|
|
```
|
2021-04-12 16:34:02 +00:00
|
|
|
## How can I send a Python object from my server to Pyodide?
|
|
|
|
|
|
|
|
The best way to do this is with pickle. If the version of Python used in the
|
|
|
|
server exactly matches the version of Python used in the client, then objects
|
|
|
|
that can be successfully pickled can be sent to the client and unpickled in
|
|
|
|
Pyodide. If the versions of Python are different then for instance sending AST
|
|
|
|
is unlikely to work since there are breaking changes to Python AST in most
|
|
|
|
Python minor versions.
|
|
|
|
|
|
|
|
Similarly when pickling Python objects defined in a Python package, the package
|
|
|
|
version needs to match exactly between the server and pyodide.
|
|
|
|
|
|
|
|
Generally, pickles are portable between architectures (here amd64 and wasm32).
|
|
|
|
The rare cases when they are not portable, for instance currently tree based
|
|
|
|
models in scikit-learn, can be considered as a bug in the upstream library.
|
|
|
|
|
|
|
|
```{admonition} Security Issues with pickle
|
|
|
|
:class: warning
|
|
|
|
|
|
|
|
Unpickling data is similar to `eval`. On any public-facing server it is a really
|
|
|
|
bad idea to unpickle any data sent from the client. For sending data from client
|
|
|
|
to server, try some other serialization format like JSON.
|
|
|
|
```
|