javascriptpythonhtmlpyodide

Pyodide runPythonAsync to html document


I am trying to run a simple pyodide example, but am not too familiar with javascript or pyodide and am not sure why the output is undefined. The statement is executed fine as I can see the correct output in the console log, but I cannot assign the output to the document.

here is the code

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        window.languagePluginUrl = 'https://cdn.jsdelivr.net/pyodide/v0.17.0a2/full/';
    </script>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.17.0a2/full/pyodide.js"></script>
</head>

<body>
  <p>You can execute any Python code. Just enter something in the box below and click the button.</p>
  <textarea id='code' style='width: 50%;' rows='10'>print('hello')</textarea>
  <button onclick='evaluatePython()'>Run</button>
  <br>
  <br>
  <div>
    Output:
  </div>
  <textarea id='output' style='width: 50%;' rows='3' disabled></textarea>

  <script>
    const output = document.getElementById("output");
    const code = document.getElementById("code");

    // show output
    function addToOutput(s) {
      output.value =  s;
      
    }

    function evaluatePython() {
      let output = pyodide.runPythonAsync(code.value)
        .then(output => addToOutput(output))
        .catch((err) => { addToOutput(err)});
    }
  </script>
</body>

</html>

I was loosely following the alternative example from here -- https://pyodide.org/en/stable/using_pyodide_from_javascript.html


Solution

  • Basically, you can implement your own function for printing. However, if you want to use exactly print you can override the function from JS.

    Update: Pyodide v0.21.0

    pyodide.globals.set('print', s => console.log(s))
    

    This will replace the python function print with the JS console.log

    const output = document.getElementById("output");
    const code = document.getElementById("code");
    
    async function evaluatePython() {
      // load imports if there are any
      await pyodide.loadPackagesFromImports(code.value)
      addToOutput(pyodide.runPython(code.value))
    }
    
    // show output
    function addToOutput(s) {
      output.value =  s;
    }
    
    let pyodide;
    
    // init pyodide
    (async () => { // enable await
      pyodide = await loadPyodide();
      
      // override default print behavior
      pyodide.globals.set('print', s => console.log(s))
    })() // call the function immediately
    <script src="https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.js"></script>
    <p>You can execute any Python code. Just enter something in the box below and click the button.</p>
      <textarea id='code' style='width: 50%;' rows='10'>print('hello')</textarea>
      <button onclick='evaluatePython()'>Run</button>
      <br>
      <br>
      <div>
        Output:
      </div>
      <textarea id='output' style='width: 50%; margin-bottom: 100px;' rows='3' disabled></textarea>

    More examples can be found here.


    Old answer (related to Pyodide v0.15.0)

    languagePluginLoader.then(()=>{
      pyodide.globals.print = s => s
    })
    

    which does nothing but returns the input parameter, since its output will then be written to the container (.then(output => addToOutput(output))). Of course, you can implement any custom behavior, such as writing the input to a container directly.

    <!DOCTYPE html>
    <html>
    <head>
        <script type="text/javascript">
            window.languagePluginUrl = 'https://cdn.jsdelivr.net/pyodide/v0.17.0a2/full/';
        </script>
        <script src="https://cdn.jsdelivr.net/pyodide/v0.17.0a2/full/pyodide.js"></script>
    </head>
    
    <body>
      <p>You can execute any Python code. Just enter something in the box below and click the button.</p>
      <textarea id='code' style='width: 50%;' rows='10'>print('hello')</textarea>
      <button onclick='evaluatePython()'>Run</button>
      <br>
      <br>
      <div>
        Output:
      </div>
      <textarea id='output' style='width: 50%;' rows='3' disabled></textarea>
    
      <script>
        const output = document.getElementById("output");
        const code = document.getElementById("code");
    
        // show output
        function addToOutput(s) {
          output.value =  s;
        }
    
        function evaluatePython() {
          let output = pyodide.runPythonAsync(code.value)
            .then(output => addToOutput(output))
            .catch((err) => { addToOutput(err)});
        }
        
        languagePluginLoader.then(()=>{
          // override default print behavior
          pyodide.globals.print = s => s
        })
      </script>
    </body>
    
    </html>