Upgrade wasm deps + fix demo

This commit is contained in:
Noa
2025-01-09 22:09:33 -06:00
committed by Jeong, YunWon
parent b7db23bbae
commit c901bc07a4
10 changed files with 121 additions and 85 deletions

22
Lib/_dummy_os.py vendored
View File

@@ -5,22 +5,30 @@ A shim of the os module containing only simple path-related utilities
try:
from os import *
except ImportError:
import abc
import abc, sys
def __getattr__(name):
raise OSError("no os specific module found")
if name in {"_path_normpath", "__path__"}:
raise AttributeError(name)
if name.isupper():
return 0
def dummy(*args, **kwargs):
import io
return io.UnsupportedOperation(f"{name}: no os specific module found")
dummy.__name__ = f"dummy_{name}"
return dummy
def _shim():
import _dummy_os, sys
sys.modules['os'] = _dummy_os
sys.modules['os.path'] = _dummy_os.path
sys.modules['os'] = sys.modules['posix'] = sys.modules[__name__]
import posixpath as path
import sys
sys.modules['os.path'] = path
del sys
sep = path.sep
supports_dir_fd = set()
supports_effective_ids = set()
supports_fd = set()
supports_follow_symlinks = set()
def fspath(path):

12
Lib/io.py vendored
View File

@@ -55,10 +55,15 @@ import _io
import abc
from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation,
open, open_code, FileIO, BytesIO, StringIO, BufferedReader,
open, open_code, BytesIO, StringIO, BufferedReader,
BufferedWriter, BufferedRWPair, BufferedRandom,
IncrementalNewlineDecoder, text_encoding, TextIOWrapper)
try:
from _io import FileIO
except ImportError:
pass
# Pretend this exception was created here.
UnsupportedOperation.__module__ = "io"
@@ -82,7 +87,10 @@ class BufferedIOBase(_io._BufferedIOBase, IOBase):
class TextIOBase(_io._TextIOBase, IOBase):
__doc__ = _io._TextIOBase.__doc__
RawIOBase.register(FileIO)
try:
RawIOBase.register(FileIO)
except NameError:
pass
for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom,
BufferedRWPair):

View File

@@ -4,24 +4,25 @@
"description": "Bindings to the RustPython library for WebAssembly",
"main": "index.js",
"dependencies": {
"codemirror": "^5.42.0",
"local-echo": "^0.2.0",
"xterm": "^3.8.0"
"@codemirror/lang-python": "^6.1.6",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.3.0",
"codemirror": "^6.0.1",
"upgrade": "^1.1.0",
"xterm-readline": "^1.1.2"
},
"devDependencies": {
"@wasm-tool/wasm-pack-plugin": "^1.1.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.4.1",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"raw-loader": "^4.0.0",
"serve": "^11.0.2",
"webpack": "^4.16.3",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
"css-loader": "^7.1.2",
"html-webpack-plugin": "^5.6.3",
"mini-css-extract-plugin": "^2.9.2",
"serve": "^14.2.4",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.0"
},
"scripts": {
"dev": "webpack-dev-server -d",
"dev": "webpack serve",
"build": "webpack",
"dist": "webpack --mode production",
"test": "webpack --mode production && cd ../tests && pytest"

View File

@@ -1,13 +1,12 @@
import asyncweb
import whlimport
whlimport.setup()
# make sys.modules['os'] a dumb version of the os module, which has posixpath
# available as os.path as well as a few other utilities, but will raise an
# OSError for anything that actually requires an OS
import _dummy_os
_dummy_os._shim()
import asyncweb
import whlimport
whlimport.setup()
@asyncweb.main
async def main():

View File

@@ -14,7 +14,6 @@
browser's devtools and play with <code>rp.pyEval('1 + 1')</code>
</p>
<div id="code-wrapper">
<textarea id="code"><%= defaultSnippet %></textarea>
<select id="snippets">
<% for (const name of snippets) { %>
<option
@@ -77,7 +76,7 @@
<a href="https://github.com/RustPython/RustPython">
<img
style="position: absolute; top: 0; right: 0; border: 0;"
src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_green_007200.png"
alt="Fork me on GitHub"
/>
</a>

View File

@@ -1,11 +1,13 @@
import './style.css';
import 'xterm/lib/xterm.css';
import CodeMirror from 'codemirror';
import 'codemirror/mode/python/python';
import 'codemirror/addon/comment/comment';
import 'codemirror/lib/codemirror.css';
import { Terminal } from 'xterm';
import LocalEchoController from 'local-echo';
import '@xterm/xterm/css/xterm.css';
import { EditorView, basicSetup } from 'codemirror';
import { keymap } from '@codemirror/view';
import { indentUnit } from '@codemirror/language';
import { indentWithTab } from '@codemirror/commands';
import { python } from '@codemirror/lang-python';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { Readline } from 'xterm-readline';
let rp;
@@ -22,23 +24,24 @@ import('rustpython')
document.getElementById('error').textContent = e;
});
const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
extraKeys: {
'Ctrl-Enter': runCodeFromTextarea,
'Cmd-Enter': runCodeFromTextarea,
'Shift-Tab': 'indentLess',
'Ctrl-/': 'toggleComment',
'Cmd-/': 'toggleComment',
Tab: (editor) => {
var spaces = Array(editor.getOption('indentUnit') + 1).join(' ');
editor.replaceSelection(spaces);
},
},
lineNumbers: true,
mode: 'text/x-python',
indentUnit: 4,
autofocus: true,
const fixedHeightEditor = EditorView.theme({
'&': { height: '100%' },
'.cm-scroller': { overflow: 'auto' },
});
const editor = new EditorView({
parent: document.getElementById('code-wrapper'),
extensions: [
basicSetup,
python(),
keymap.of(
{ key: 'Ctrl-Enter', mac: 'Cmd-Enter', run: runCodeFromTextarea },
indentWithTab,
),
indentUnit.of(' '),
fixedHeightEditor,
],
});
editor.focus();
const consoleElement = document.getElementById('console');
const errorElement = document.getElementById('error');
@@ -48,7 +51,7 @@ function runCodeFromTextarea() {
consoleElement.value = '';
errorElement.textContent = '';
const code = editor.getValue();
const code = editor.state.doc.toString();
try {
rp.pyExec(code, {
stdout: (output) => {
@@ -78,18 +81,25 @@ function updateSnippet() {
// the require here creates a webpack context; it's fine to use it
// dynamically.
// https://webpack.js.org/guides/dependency-management/
const { default: snippet } = require(
`raw-loader!../snippets/${selected}.py`,
);
const snippet = require(`../snippets/${selected}.py?raw`);
editor.setValue(snippet);
runCodeFromTextarea();
editor.dispatch({
changes: { from: 0, to: editor.state.doc.length, insert: snippet },
});
}
function updateSnippetAndRun() {
updateSnippet();
requestAnimationFrame(runCodeFromTextarea);
}
updateSnippet();
const term = new Terminal();
const readline = new Readline();
const fitAddon = new FitAddon();
term.loadAddon(readline);
term.loadAddon(fitAddon);
term.open(document.getElementById('terminal'));
const localEcho = new LocalEchoController(term);
fitAddon.fit();
let terminalVM;
@@ -107,40 +117,33 @@ finally:
}
async function readPrompts() {
let continuing = false;
let continuing = '';
while (true) {
const ps1 = getPrompt('ps1');
const ps2 = getPrompt('ps2');
let input;
let input = await readline.read(getPrompt(continuing ? 'ps2' : 'ps1'));
if (input.endsWith('\n')) input = input.slice(0, -1);
if (continuing) {
const prom = localEcho.read(ps2, ps2);
localEcho._activePrompt.prompt = ps1;
localEcho._input = localEcho.history.entries.pop() + '\n';
localEcho._cursor = localEcho._input.length;
localEcho._active = true;
input = await prom;
if (!input.endsWith('\n')) continue;
} else {
input = await localEcho.read(ps1, ps2);
input = continuing += '\n' + input;
if (!continuing.endsWith('\n')) continue;
}
try {
console.log([input]);
terminalVM.execSingle(input);
} catch (err) {
if (err.canContinue) {
continuing = true;
continuing = input;
continue;
} else if (err instanceof WebAssembly.RuntimeError) {
err = window.__RUSTPYTHON_ERROR || err;
}
localEcho.println(err);
readline.print('' + err);
}
continuing = false;
continuing = '';
}
}
function onReady() {
snippets.addEventListener('change', updateSnippet);
snippets.addEventListener('change', updateSnippetAndRun);
document
.getElementById('run-btn')
.addEventListener('click', runCodeFromTextarea);
@@ -148,7 +151,7 @@ function onReady() {
runCodeFromTextarea();
terminalVM = rp.vmStore.init('term_vm');
terminalVM.setStdout((data) => localEcho.print(data));
terminalVM.setStdout((data) => readline.print(data));
readPrompts().catch((err) => console.error(err));
// so that the test knows that we're ready

View File

@@ -3,7 +3,7 @@ textarea {
resize: vertical;
}
#code,
#code-wrapper,
#console {
height: 30vh;
width: calc(100% - 3px);

View File

@@ -1,7 +1,6 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');
const fs = require('fs');
@@ -12,6 +11,7 @@ module.exports = (env = {}) => {
output: {
path: path.join(__dirname, 'dist'),
filename: 'index.js',
clean: true,
},
mode: 'development',
resolve: {
@@ -28,10 +28,17 @@ module.exports = (env = {}) => {
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
resourceQuery: '?raw',
type: 'asset/source',
},
{
test: /\.wasm$/,
type: 'webassembly/async',
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.ejs',
@@ -51,6 +58,9 @@ module.exports = (env = {}) => {
filename: 'styles.css',
}),
],
experiments: {
asyncWebAssembly: true,
},
};
if (!env.noWasmPack) {
config.plugins.push(

View File

@@ -3,7 +3,7 @@
use crate::js_module;
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, SyntaxError, Uint8Array};
use rustpython_parser::ParseErrorType;
use rustpython_parser::{lexer::LexicalErrorType, ParseErrorType};
use rustpython_vm::{
builtins::PyBaseExceptionRef,
compiler::{CompileError, CompileErrorType},
@@ -258,7 +258,15 @@ pub fn syntax_err(err: CompileError) -> SyntaxError {
&"col".into(),
&(err.location.unwrap().column.get()).into(),
);
let can_continue = matches!(&err.error, CompileErrorType::Parse(ParseErrorType::Eof));
let can_continue = matches!(
&err.error,
CompileErrorType::Parse(
ParseErrorType::Eof
| ParseErrorType::Lexical(LexicalErrorType::Eof)
| ParseErrorType::Lexical(LexicalErrorType::IndentationError)
| ParseErrorType::UnrecognizedToken(rustpython_parser::Tok::Dedent, _)
)
);
let _ = Reflect::set(&js_err, &"canContinue".into(), &can_continue.into());
js_err
}