mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 11:14:38 +08:00
wasm: add stderr support (#8318)
Assisted-by: Codex:gpt-5.5 Signed-off-by: Jiwoo Ahn <ikwydls1314@gmail.com>
This commit is contained in:
1 parent
875400413f
commit
3290f287fd
7 files changed
+73
-24
No files matched your search
@@ -64,8 +64,7 @@ async def _main_wrapper(coro):
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
# TODO: sys.stderr on wasm
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def _resolve(prom):
|
||||
|
||||
@@ -34,6 +34,9 @@ pyEval(code, options?);
|
||||
- `stdout?`: `"console" | ((out: string) => void) | null`: A function to replace the
|
||||
native print function, and it will be `console.log` when giving `undefined`
|
||||
or "console", and it will be a dumb function when giving null.
|
||||
- `stderr?`: `"console" | ((out: string) => void) | null`: A function to replace
|
||||
`sys.stderr`, and it will be `console.error` when giving `undefined` or
|
||||
"console".
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ pub mod eval {
|
||||
};
|
||||
|
||||
vm.set_stdout(Reflect::get(&options, &"stdout".into())?)?;
|
||||
vm.set_stderr(Reflect::get(&options, &"stderr".into())?)?;
|
||||
|
||||
if let Some(js_vars) = js_vars {
|
||||
vm.add_to_scope("js_vars".into(), js_vars.into())?;
|
||||
@@ -93,6 +94,8 @@ pub mod eval {
|
||||
/// - `stdout?`: `"console" | ((out: string) => void) | null`: A function to replace the
|
||||
/// native print native print function, and it will be `console.log` when giving
|
||||
/// `undefined` or "console", and it will be a dumb function when giving null.
|
||||
/// - `stderr?`: `"console" | ((out: string) => void) | null`: A function to replace
|
||||
/// `sys.stderr`, and it will be `console.error` when giving `undefined` or "console".
|
||||
#[wasm_bindgen(js_name = pyEval)]
|
||||
pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue> {
|
||||
run_py(source, options, Mode::Eval)
|
||||
|
||||
+43
-15
@@ -249,31 +249,59 @@ impl WASMVirtualMachine {
|
||||
|
||||
#[wasm_bindgen(js_name = setStdout)]
|
||||
pub fn set_stdout(&self, stdout: JsValue) -> Result<(), JsValue> {
|
||||
self.set_stdstream(
|
||||
"stdout",
|
||||
"JSStdout",
|
||||
stdout,
|
||||
wasm_builtins::sys_stdout_write_console,
|
||||
)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = setStderr)]
|
||||
pub fn set_stderr(&self, stderr: JsValue) -> Result<(), JsValue> {
|
||||
self.set_stdstream(
|
||||
"stderr",
|
||||
"JSStderr",
|
||||
stderr,
|
||||
wasm_builtins::sys_stderr_write_console,
|
||||
)
|
||||
}
|
||||
|
||||
fn set_stdstream(
|
||||
&self,
|
||||
attr: &'static str,
|
||||
class_name: &'static str,
|
||||
stream: JsValue,
|
||||
console_write: fn(&str, &VirtualMachine) -> PyResult<()>,
|
||||
) -> Result<(), JsValue> {
|
||||
self.with_vm(|vm, _| {
|
||||
fn error() -> JsValue {
|
||||
TypeError::new("Unknown stdout option, please pass a function or 'console'").into()
|
||||
fn error(attr: &str) -> JsValue {
|
||||
TypeError::new(&format!(
|
||||
"Unknown {attr} option, please pass a function or 'console'"
|
||||
))
|
||||
.into()
|
||||
}
|
||||
use wasm_builtins::make_stdout_object;
|
||||
let stdout: PyObjectRef = if let Some(s) = stdout.as_string() {
|
||||
use wasm_builtins::make_stdstream_object;
|
||||
let stream: PyObjectRef = if let Some(s) = stream.as_string() {
|
||||
match s.as_str() {
|
||||
"console" => make_stdout_object(vm, wasm_builtins::sys_stdout_write_console),
|
||||
_ => return Err(error()),
|
||||
"console" => make_stdstream_object(vm, class_name, console_write),
|
||||
_ => return Err(error(attr)),
|
||||
}
|
||||
} else if stdout.is_function() {
|
||||
let func = js_sys::Function::from(stdout);
|
||||
make_stdout_object(vm, move |data, vm| {
|
||||
} else if stream.is_function() {
|
||||
let func = js_sys::Function::from(stream);
|
||||
make_stdstream_object(vm, class_name, move |data, vm| {
|
||||
func.call1(&JsValue::UNDEFINED, &data.into())
|
||||
.map_err(|err| convert::js_py_typeerror(vm, err))?;
|
||||
Ok(())
|
||||
})
|
||||
} else if stdout.is_null() {
|
||||
make_stdout_object(vm, |_, _| Ok(()))
|
||||
} else if stdout.is_undefined() {
|
||||
make_stdout_object(vm, wasm_builtins::sys_stdout_write_console)
|
||||
} else if stream.is_null() {
|
||||
make_stdstream_object(vm, class_name, |_, _| Ok(()))
|
||||
} else if stream.is_undefined() {
|
||||
make_stdstream_object(vm, class_name, console_write)
|
||||
} else {
|
||||
return Err(error());
|
||||
return Err(error(attr));
|
||||
};
|
||||
vm.sys_module.set_attr("stdout", stdout, vm).unwrap();
|
||||
vm.sys_module.set_attr(attr, stream, vm).unwrap();
|
||||
Ok(())
|
||||
})?
|
||||
}
|
||||
|
||||
@@ -16,19 +16,20 @@ pub fn sys_stdout_write_console(data: &str, _vm: &VirtualMachine) -> PyResult<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn make_stdout_object(
|
||||
pub fn sys_stderr_write_console(data: &str, _vm: &VirtualMachine) -> PyResult<()> {
|
||||
console::error_1(&data.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn make_stdstream_object(
|
||||
vm: &VirtualMachine,
|
||||
name: &'static str,
|
||||
write_f: impl Fn(&str, &VirtualMachine) -> PyResult<()> + 'static,
|
||||
) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
// there's not really any point to storing this class so that there's a consistent type object,
|
||||
// we just want a half-decent repr() output
|
||||
let cls = PyRef::leak(py_class!(
|
||||
ctx,
|
||||
"JSStdout",
|
||||
vm.ctx.types.object_type.to_owned(),
|
||||
{}
|
||||
));
|
||||
let cls = PyRef::leak(py_class!(ctx, name, vm.ctx.types.object_type.to_owned(), {}));
|
||||
let write_method = vm.new_method(
|
||||
"write",
|
||||
cls,
|
||||
|
||||
@@ -159,6 +159,7 @@ function onReady() {
|
||||
|
||||
terminalVM = rp.vmStore.init('term_vm');
|
||||
terminalVM.setStdout((data) => readline.print(data));
|
||||
terminalVM.setStderr((data) => readline.print(data));
|
||||
readPrompts().catch((err) => console.error(err));
|
||||
|
||||
// so that the test knows that we're ready
|
||||
|
||||
@@ -19,3 +19,17 @@ def test_exec_single_mode(wdriver):
|
||||
"""
|
||||
)
|
||||
assert stdout == "2\n4\n"
|
||||
|
||||
|
||||
def test_exec_stderr_option(wdriver):
|
||||
stderr = wdriver.execute_script(
|
||||
"""
|
||||
let output = "";
|
||||
save_output = function(text) {
|
||||
output += text
|
||||
};
|
||||
window.rp.pyExec('import sys; print("err", file=sys.stderr)', {stderr: save_output});
|
||||
return output;
|
||||
"""
|
||||
)
|
||||
assert stderr == "err\n"
|
||||
Reference in new issue
Block a user