4.4 KiB
Frequently Asked Questions (FAQ)
How can I load external Python files in Pyodide?
The two possible solutions are,
- include these files in a Python package, build a pure Python wheel with
python setup.py bdist_wheel
and {ref}load it with micropip <micropip-installing-from-arbitrary-urls>
. - fetch the Python code as a string and evaluate it in Python,
pyodide.runPython(await fetch('https://some_url/...'))
In both cases, files need to be served with a web server and cannot be loaded from local file system.
Why can't I load files from the local file system?
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 supported in Chrome but not in Firefox. There is a discussion about implementing it for Firefox here.
How can I change the behavior of runPython
and runPythonAsync
?
The definitions of runPython
and runPythonAsync
are very simple:
function runPython(code){
pyodide.pyodide_py.eval_code(code, pyodide.globals);
}
async function runPythonAsync(code, messageCallback, errorCallback) {
await pyodide.loadPackagesFromImports(code, messageCallback, errorCallback);
return pyodide.runPython(code);
};
To make your own version of runPython
:
pyodide.runPython(`
import pyodide
old_eval_code = pyodide.eval_code
def my_eval_code(code, ns):
extra_info = None
result = old_eval_code(code, ns)
return [ns["extra_info"], result]
`)
function myRunPython(code){
return pyodide.globals.get("my_eval_code")(code, pyodide.globals);
}
function myAsyncRunPython(code){
await pyodide.loadPackagesFromImports(code, messageCallback, errorCallback);
return pyodide.myRunPython(code, pyodide.globals);
}
Then pyodide.myRunPython("2+7")
returns [None, 9]
and
pyodide.myRunPython("extra_info='hello' ; 2 + 2")
returns ['hello', 4]
.
If you want to change which packages loadPackagesFromImports
loads, you can
monkey patch pyodide-py.find_imports
which takes code
as an argument
and returns a list of packages imported.
How can I execute code in a custom namespace?
The second argument to {any}pyodide.eval_code
is a global namespace to execute the code in.
The namespace is a Python dictionary.
let my_namespace = pyodide.globals.dict();
pyodide.pyodide_py.eval_code(`x = 1 + 1`, my_namespace);
pyodide.pyodide_py.eval_code(`y = x ** x`, my_namespace);
my_namespace.y; // ==> 4
This effectively runs the code in "module scope". Like the
Python eval
function
you can provide a third argument to eval_code
to specify a separate locals
dict to
run code in "function scope".
How to detect that code is run with Pyodide?
At run time, you can detect that a code is running with Pyodide using,
import sys
if "pyodide" in sys.modules:
# running in Pyodide
More generally you can detect Python built with Emscripten (which includes Pyodide) with,
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
the Pyodide build system works. It first compiles packages with the host compiler
(e.g. gcc) and then re-runs the compilation commands with emsdk. So the setup.py
is
never run inside the Pyodide environement.
To detect Pyodide, at build time use,
import os
if "PYODIDE" in os.environ:
# building for Pyodide
We used to use the environment variable PYODIDE_BASE_URL
for this purpose,
but this usage is deprecated.
How do I create custom Python packages from Javascript?
Put a collection of functions into a Javascript object and use pyodide.registerJsModule
:
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,
},
};
pyodide.registerJsModule("my_js_module", my_module);
You can import your package like a normal Python package:
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