pyodide/docs/usage/faq.md

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