如何在Pyodide文件系统中运行我编写的Python文件?

huangapple go评论79阅读模式
英文:

How can I run python files that I wrote into Pyodides file system?

问题

我正在编写一个基于Pyodide的文本编辑器,该编辑器将在用户在编辑器中输入时运行测试脚本,使用ace-editor,我使用pyodides的pyodide.FS.writeFile()函数将用户输入的内容写入python文件中。我对我预先创建的测试文件也做同样的操作,然而,我在疑惑的是pyodide是否能够运行我写入pyodides虚拟文件系统的文件,因为我看不到它为什么不能运行的理由。下面是处理这部分的JavaScript文件,记住我正在使用ace:

//fileSaver is used to save the code to a file and download it 
const fileSaver = require('file-saver');
// Setup ace variables and the output pane for pyodide
var editor = ace.edit("editor");
var output_pane;
// The following line will essentially be the "file path" input for the RST directive, or 
// we can figure out how to pass arguments into an iframe if thats even possible
var testFilePath = '/_static/test_files/test.py';

loadPyodide().then((pyodide) => {
    // pyodide is now ready to use...
    globalThis.pyodide = pyodide;
    appendOutput('Python ready.\n');
});

function appendOutput(msg) {
    // used to add program output to the textarea
    output_pane.value = output_pane.value + '\n' + msg;
    output_pane.scrollTop = output_pane.scrollHeight;
}

function configEditor(){
    // configure the ace editor to make it usable
    editor = ace.edit("editor");
    editor.setTheme("ace/theme/xcode");
    editor.session.setMode("ace/mode/python");
    editor.setShowPrintMargin(false);
    editor.setBehavioursEnabled(true);
    editor.setFontSize(13);
    //Fix indentation issue with ace editor, not really the best solution but it works
    var code = editor.getValue();
    var lines = code.split("\n");
    var minIndent = null;
    for (var i = 0; i < lines.length; i++) {
        var line = lines[i];
        if (line.trim().length > 0) {
            var indent = line.search(/\S/);
            if (minIndent === null || (indent !== -1 && indent < minIndent)) {
                minIndent = indent;
            }
        }
    }
    if (minIndent !== null) {
        for (var i = 0; i < lines.length; i++) {
            if (lines[i].trim().length > 0) {
                lines[i] = lines[i].slice(minIndent);
            }
        }
        code = lines.join('\n');
        editor.setValue(code);
    }
}

function openCode(filePathToUse) {
    getCode(filePathToUse)
      .then(code => {
        var modelist = ace.require("ace/ext/modelist");
        var modeName = modelist.getModeForPath(filePathToUse).mode;
        editor.session.setMode(modeName);
        editor.session.setValue(code);
      })
      .catch(error => {
        console.error('Error occurred while opening the code:', error);
      });
  }

async function runCode(code_to_run) {
    // Run the code thats within the editor so students can test
    if(code_to_run == editor.getValue()){
        console.logs = [];

        let promise = new Promise((resolve, reject) => {
            window.pyodide.runPython(code_to_run)
            resolve(true)
        }).catch(err => {
            console.log(err);
            appendOutput(console.logs.join('\n')); 
        });
    
        let result = await promise;
        if (result) { 
            appendOutput(console.logs.join('\n')); 
        }
    } else {
        // run the code from the editor and display the output in the textarea

        let data = editor.getValue(); 
        let testData = code_to_run;
        window.pyodide.FS.writeFile("/challenge.py", data);
        window.pyodide.FS.writeFile("/test.py", testData);
        let challengeFile = pyodide.FS.readFile("/challenge.py");
        let testFile = pyodide.FS.readFile("/test.py");
        console.log(challengeFile);
        console.log(testFile);
        
        console.logs = [];

        let promise = new Promise((resolve, reject) => {
            window.pyodide.runPython(testCodeString)
            resolve(true)
        }).catch(err => {
            console.log(err);
            appendOutput(console.logs.join('\n')); 
        });

        let result = await promise;
        if (result) { 
            appendOutput(console.logs.join('\n')); 
        }
    }
}

function saveCode(code) {
    var blob = new Blob([code], { type: "text/plain;charset=utf-8" });
    window.saveAs(blob, 'challenge.py');
}

//make a function getCode that takes in a file path and returns the code in that file as a string to use in ace
async function getCode(codeToGet) {
    try {
      const response = await fetch(codeToGet);
      const data = await response.text();
      return data;
    } catch (error) {
      console.error('Error occurred while opening the code:', error);
    }
  }


//codeToSwitch will be a file path
function switchFile(codeToSwitch) {
    getCode(codeToSwitch)
    .then(code => {
        var EditSession = ace.require("ace/edit_session").EditSession;
        var oldSession = editor.getSession();
        //change to new file
        var newSession = new EditSession(code, "ace/mode/python");
        editor.setSession(newSession);
    })
    .catch(error => {
      console.error('Error occurred while opening the code:', error);
    });
}

document.addEventListener('DOMContentLoaded', (event) => {

    output_pane = document.getElementById("output");
    // Add event listeners for downloading code
    document.getElementById("downloadButton").addEventListener('click', function () {
        saveCode(editor.getValue());
    });

    // Add event listeners for switching files
    document.getElementById("switchButton").addEventListener('click', function () {
        switchFile(testFilePath);
    });

    document.getElementById("runButton").addEventListener('click', function () {
        runCode(editor.getValue());
    });
    
    // Add event listeners for running code
    document.getElementById("run_code").addEventListener('click', function () {
        //Run the getcode function to get the code from the editor
        getCode(testFilePath)
        .then(code => {
            runCode(code);
        }) 
        .catch(error => {
            console.error('Error occurred while opening the code:', error);
        });
    });

    // Capture the output from Pyodide and add it to an array
    console.stdlog = console.log.bind(console);
    console.logs = [];
    console.log = function(){
        console.logs.push(Array.from(arguments));
        console.stdlog.apply(console, arguments);
    }
    
    configEditor();
});

要注意的主要内容是runCode()函数,因为这是应该进行更改的地方。我首先获取用户写的内容,将其放入pyodides的虚拟

英文:

I am writing a Pyodide based text editor that will run test scripts, when a user types into the editor, using ace-editor, I grab whatever the user types and write it into a python file using pyodides pyodide.FS.writeFile() function, I do the same with the premade test file I have, however, I am struggling to understand if pyodide can run the files I write into pyodides virtual file system as I don't see a reason why it shouldnt. Here is the javascript file that handles this below, keep in mind I am using ace:

//fileSaver is used to save the code to a file and download it 
const fileSaver = require(&#39;file-saver&#39;);
// Setup ace variables and the output pane for pyodide
var editor = ace.edit(&quot;editor&quot;);
var output_pane;
// The following line will essentially be the &quot;file path&quot; input for the RST directive, or 
// we can figure out how to pass arguments into an iframe if thats even possible
var testFilePath = &#39;/_static/test_files/test.py&#39;;

loadPyodide().then((pyodide) =&gt; {
    // pyodide is now ready to use...
    globalThis.pyodide = pyodide;
    appendOutput(&#39;Python ready.\n&#39;);
});

function appendOutput(msg) {
    // used to add program output to the textarea
    output_pane.value = output_pane.value + &#39;\n&#39; + msg;
    output_pane.scrollTop = output_pane.scrollHeight;
}

function configEditor(){
    // configure the ace editor to make it usable
    editor = ace.edit(&quot;editor&quot;);
    editor.setTheme(&quot;ace/theme/xcode&quot;);
    editor.session.setMode(&quot;ace/mode/python&quot;);
    editor.setShowPrintMargin(false);
    editor.setBehavioursEnabled(true);
    editor.setFontSize(13);
    //Fix indentation issue with ace editor, not really the best solution but it works
    var code = editor.getValue();
    var lines = code.split(&quot;\n&quot;);
    var minIndent = null;
    for (var i = 0; i &lt; lines.length; i++) {
        var line = lines[i];
        if (line.trim().length &gt; 0) {
            var indent = line.search(/\S/);
            if (minIndent === null || (indent !== -1 &amp;&amp; indent &lt; minIndent)) {
                minIndent = indent;
            }
        }
    }
    if (minIndent !== null) {
        for (var i = 0; i &lt; lines.length; i++) {
            if (lines[i].trim().length &gt; 0) {
                lines[i] = lines[i].slice(minIndent);
            }
        }
        code = lines.join(&#39;\n&#39;);
        editor.setValue(code);
    }
}

function openCode(filePathToUse) {
    getCode(filePathToUse)
      .then(code =&gt; {
        var modelist = ace.require(&quot;ace/ext/modelist&quot;);
        var modeName = modelist.getModeForPath(filePathToUse).mode;
        editor.session.setMode(modeName);
        editor.session.setValue(code);
      })
      .catch(error =&gt; {
        console.error(&#39;Error occurred while opening the code:&#39;, error);
      });
  }

async function runCode(code_to_run) {
    // Run the code thats within the editor so students can test
    if(code_to_run == editor.getValue()){
        console.logs = [];

        let promise = new Promise((resolve, reject) =&gt; {
            window.pyodide.runPython(code_to_run)
            resolve(true)
        }).catch(err =&gt; {
            console.log(err);
            appendOutput(console.logs.join(&#39;\n&#39;)); 
        });
    
        let result = await promise;
        if (result) { 
            appendOutput(console.logs.join(&#39;\n&#39;)); 
        }
    } else {
        // run the code from the editor and display the output in the textarea

        let data = editor.getValue(); 
        let testData = code_to_run;
        window.pyodide.FS.writeFile(&quot;/challenge.py&quot;, data);
        window.pyodide.FS.writeFile(&quot;/test.py&quot;, testData);
        let challengeFile = pyodide.FS.readFile(&quot;/challenge.py&quot;);
        let testFile = pyodide.FS.readFile(&quot;/test.py&quot;);
        console.log(challengeFile);
        console.log(testFile);
        
        console.logs = [];

        let promise = new Promise((resolve, reject) =&gt; {
            window.pyodide.runPython(testCodeString)
            resolve(true)
        }).catch(err =&gt; {
            console.log(err);
            appendOutput(console.logs.join(&#39;\n&#39;)); 
        });

        let result = await promise;
        if (result) { 
            appendOutput(console.logs.join(&#39;\n&#39;)); 
        }
    }
}

function saveCode(code) {
    var blob = new Blob([code], { type: &quot;text/plain;charset=utf-8&quot; });
    window.saveAs(blob, &#39;challenge.py&#39;);
}

//make a function getCode that takes in a file path and returns the code in that file as a string to use in ace
async function getCode(codeToGet) {
    try {
      const response = await fetch(codeToGet);
      const data = await response.text();
      return data;
    } catch (error) {
      console.error(&#39;Error occurred while opening the code:&#39;, error);
    }
  }


//codeToSwitch will be a file path
function switchFile(codeToSwitch) {
    getCode(codeToSwitch)
    .then(code =&gt; {
        var EditSession = ace.require(&quot;ace/edit_session&quot;).EditSession;
        var oldSession = editor.getSession();
        //change to new file
        var newSession = new EditSession(code, &quot;ace/mode/python&quot;);
        editor.setSession(newSession);
    })
    .catch(error =&gt; {
      console.error(&#39;Error occurred while opening the code:&#39;, error);
    });
}

document.addEventListener(&#39;DOMContentLoaded&#39;, (event) =&gt; {

    output_pane = document.getElementById(&quot;output&quot;);
    // Add event listeners for downloading code
    document.getElementById(&quot;downloadButton&quot;).addEventListener(&#39;click&#39;, function () {
        saveCode(editor.getValue());
    });

    // Add event listeners for switching files
    document.getElementById(&quot;switchButton&quot;).addEventListener(&#39;click&#39;, function () {
        switchFile(testFilePath);
    });

    document.getElementById(&quot;runButton&quot;).addEventListener(&#39;click&#39;, function () {
        runCode(editor.getValue());
    });
    
    // Add event listeners for running code
    document.getElementById(&quot;run_code&quot;).addEventListener(&#39;click&#39;, function () {
        //Run the getcode function to get the code from the editor
        getCode(testFilePath)
        .then(code =&gt; {
            runCode(code);
        }) 
        .catch(error =&gt; {
            console.error(&#39;Error occurred while opening the code:&#39;, error);
        });
    });

    // Capture the output from Pyodide and add it to an array
    console.stdlog = console.log.bind(console);
    console.logs = [];
    console.log = function(){
        console.logs.push(Array.from(arguments));
        console.stdlog.apply(console, arguments);
    }
    
    configEditor();
});

The main thing to pay attention to is the runCode() function as that is where the changes should be made. I first grab whatever the user wrote, put it into pyodides virtual file system, do the same with a premade test file, then in the variable testCodeString I want to just run the test file. I tried import test but that didnt work. How would I be able to accomplish this?

答案1

得分: 1

以下是您提供的内容的中文翻译:

在研究了Pyodide如何处理包之后,可以使用exec关键字将本地文件写入文件系统,然后通过以下方式在代码中确保在编辑代码时测试输出会发生变化:

console.logs = [];

var data = editor.getValue(); 
var testData = code_to_run;
window.pyodide.FS.writeFile("challenge.py", data);
window.pyodide.FS.writeFile("test.py", testData);
        
let promise = new Promise((resolve, reject) => {
    window.pyodide.runPython(`
        exec(open('test.py').read())
        import sys
        sys.modules.pop("challenge", None)
        sys.modules.pop("test", None)
`)
resolve(true)
   }).catch(err => {
        console.log(err);
        appendOutput(console.logs.join('\n')); 
   });
        
let result = await promise;
if (result) { 
    appendOutput(console.logs.join('\n')); 
}

希望这对您有所帮助。

英文:

After looking into how Pyodide handles packages, local files can be written to the file system to be run by using the exec keyword, however, to ensure that test output will change when editing code, then you need to clear sys.modules like so in the code:


console.logs = [];

var data = editor.getValue(); 
var testData = code_to_run;
window.pyodide.FS.writeFile(&quot;challenge.py&quot;, data);
window.pyodide.FS.writeFile(&quot;test.py&quot;, testData);
        
let promise = new Promise((resolve, reject) =&gt; {
    window.pyodide.runPython(`
        exec(open(&#39;test.py&#39;).read())
        import sys
        sys.modules.pop(&quot;challenge&quot;, None)
        sys.modules.pop(&quot;test&quot;, None)
`)
resolve(true)
   }).catch(err =&gt; {
        console.log(err);
        appendOutput(console.logs.join(&#39;\n&#39;)); 
   });
        
let result = await promise;
if (result) { 
    appendOutput(console.logs.join(&#39;\n&#39;)); 
}

huangapple
  • 本文由 发表于 2023年5月26日 10:25:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76337288.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定