From bac5aa544779179de42179f3b718f996a3cf2dbd Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 11:12:24 -0700 Subject: [PATCH 1/9] README update --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e472bd9f6a..b11c664e89 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,11 @@ You can build the WebAssembly WASI file with: cargo build --release --target wasm32-wasip1 --features="freeze-stdlib" ``` -> Note: we use the `freeze-stdlib` to include the standard library inside the binary. You also have to run once `rustup target add wasm32-wasip1`. +> [!NOTE] +> we use the `freeze-stdlib` to include the standard library inside the binary. You also have to run once `rustup target add wasm32-wasip1`. + +> [!IMPORTANT] +> Both `wasip1` and `wasip2` are supported, but `p2` requires nightly to build. ### JIT (Just in time) compiler From 5f4a29aab433a613833239f35437b9d9085479d8 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 11:13:06 -0700 Subject: [PATCH 2/9] refactor browser module --- wasm/lib/Lib/browser/__init__.py | 34 +++++++++++ wasm/lib/Lib/browser/util.py | 11 ++++ wasm/lib/Lib/browser/window.py | 99 ++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 wasm/lib/Lib/browser/__init__.py create mode 100644 wasm/lib/Lib/browser/util.py create mode 100644 wasm/lib/Lib/browser/window.py diff --git a/wasm/lib/Lib/browser/__init__.py b/wasm/lib/Lib/browser/__init__.py new file mode 100644 index 0000000000..9751155116 --- /dev/null +++ b/wasm/lib/Lib/browser/__init__.py @@ -0,0 +1,34 @@ +from _browser import ( + Document, + Element, + load_module, +) + +from _js import JSValue, Promise +from .window import alert, atob, btoa, confirm, prompt, request_animation_frame, cancel_animation_frame + +from .util import jsstr, jsclosure, jsclosure_once, jsfloat, NULL, UNDEFINED + +__all__ = [ + "jsstr", + "jsclosure", + "jsclosure_once", + "jsfloat", + "NULL", + "UNDEFINED", + "alert", + "atob", + "btoa", + "confirm", + "prompt", + "fetch", + "request_animation_frame", + "cancel_animation_frame", + "Document", + "Element", + "load_module", + "JSValue", + "Promise", +] + + diff --git a/wasm/lib/Lib/browser/util.py b/wasm/lib/Lib/browser/util.py new file mode 100644 index 0000000000..e274598cbd --- /dev/null +++ b/wasm/lib/Lib/browser/util.py @@ -0,0 +1,11 @@ +from _window import window # type: ignore + +jsstr = window.new_from_str +jsclosure = window.new_closure +jsclosure_once = window.new_closure_once + +def jsfloat(n): + return window.new_from_float(float(n)) + +UNDEFINED = window.undefined() +NULL = window.null() diff --git a/wasm/lib/Lib/browser/window.py b/wasm/lib/Lib/browser/window.py new file mode 100644 index 0000000000..d43a06f933 --- /dev/null +++ b/wasm/lib/Lib/browser/window.py @@ -0,0 +1,99 @@ +from _window import window as Window # type: ignore +from .util import jsint, jsstr, UNDEFINED + +__all__ = [ + "Window", + "alert", + "atob", + "btoa", + "cancel_animation_frame", + "close", + "confirm", + "fetch", + "focus", + "print", + "prompt", + "request_animation_frame", + "resize_by", + "resize_to", +] + +_alert = Window.get_prop("alert") + +def alert(msg = None): + if msg is None: + return _alert.call() + if type(msg) != str: + raise TypeError("msg must be a string") + _alert.call(jsstr(msg)) + +_atob = Window.get_prop("atob") + +def atob(data): + if type(data) != str: + raise TypeError("data must be a string") + return _atob.call(jsstr(data)).as_str() + +_btoa = Window.get_prop("btoa") +def btoa(data): + if type(data) != str: + raise TypeError("data must be a string") + return _btoa.call(jsstr(data)).as_str() + + +from _browser import cancel_animation_frame + +_close = Window.get_prop("close") +def close(): + return _close.call() + +_confirm = Window.get_prop("confirm") +def confirm(msg): + if type(msg) != str: + raise TypeError("msg must be a string") + return _confirm.call(jsstr(msg)).as_bool() + +from _browser import fetch + +_focus = Window.get_prop("focus") +def focus(): + return _focus.call() + +_print = Window.get_prop("print") +def print(): + return _print.call() + +_prompt = Window.get_prop("prompt") +def prompt(msg, default_val=None): + if type(msg) != str: + raise TypeError("msg must be a string") + if default_val is not None and type(default_val) != str: + raise TypeError("default_val must be a string") + + return _prompt.call( + jsstr(msg), jsstr(default_val) if default_val else UNDEFINED + ).as_str() + +from _browser import request_animation_frame + +_resize_by = Window.get_prop("resizeBy") + +def resize_by(x, y): + if type(x) != int: + raise TypeError("x must be an int") + if type(y) != int: + raise TypeError("y must be an int") + _resize_by.call(jsint(x), jsint(y)) + +_resize_to = Window.get_prop("resizeTo") + +def resize_to(x, y): + if type(x) != int: + raise TypeError("x must be an int") + if type(y) != int: + raise TypeError("y must be an int") + _resize_to.call(jsint(x), jsint(y)) + +_stop = Window.get_prop("stop") +def stop(): + return _stop.call() From a9c9a4674abc26225d7b3d905a140169720bcdf8 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 11:13:33 -0700 Subject: [PATCH 3/9] remove old browser module --- wasm/lib/Lib/browser.py | 76 ----------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 wasm/lib/Lib/browser.py diff --git a/wasm/lib/Lib/browser.py b/wasm/lib/Lib/browser.py deleted file mode 100644 index 515fe2e673..0000000000 --- a/wasm/lib/Lib/browser.py +++ /dev/null @@ -1,76 +0,0 @@ -from _browser import ( - fetch, - request_animation_frame, - cancel_animation_frame, - Document, - Element, - load_module, -) - -from _js import JSValue, Promise -from _window import window - -__all__ = [ - "jsstr", - "jsclosure", - "jsclosure_once", - "jsfloat", - "NULL", - "UNDEFINED", - "alert", - "confirm", - "prompt", - "fetch", - "request_animation_frame", - "cancel_animation_frame", - "Document", - "Element", - "load_module", - "JSValue", - "Promise", -] - - -jsstr = window.new_from_str -jsclosure = window.new_closure -jsclosure_once = window.new_closure_once -_jsfloat = window.new_from_float - -UNDEFINED = window.undefined() -NULL = window.null() - - -def jsfloat(n): - return _jsfloat(float(n)) - - -_alert = window.get_prop("alert") - - -def alert(msg): - if type(msg) != str: - raise TypeError("msg must be a string") - _alert.call(jsstr(msg)) - - -_confirm = window.get_prop("confirm") - - -def confirm(msg): - if type(msg) != str: - raise TypeError("msg must be a string") - return _confirm.call(jsstr(msg)).as_bool() - - -_prompt = window.get_prop("prompt") - - -def prompt(msg, default_val=None): - if type(msg) != str: - raise TypeError("msg must be a string") - if default_val is not None and type(default_val) != str: - raise TypeError("default_val must be a string") - - return _prompt.call( - jsstr(msg), jsstr(default_val) if default_val else UNDEFINED - ).as_str() From e47acc6af9836e06e26c63ab5ab45f8e86e88ef6 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 11:14:17 -0700 Subject: [PATCH 4/9] improve DOM api and add window to _browser --- wasm/lib/src/browser_module.rs | 77 ++++++++++++++++++++++++++++++---- wasm/lib/src/vm_class.rs | 14 ------- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f8d1b2ebc3..f2e510823c 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,18 +1,14 @@ use rustpython_vm::VirtualMachine; pub(crate) use _browser::make_module; +use crate::wasm_builtins::window; #[pymodule] mod _browser { use crate::{convert, js_module::PyPromise, vm_class::weak_vm, wasm_builtins::window}; use js_sys::Promise; use rustpython_vm::{ - PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, - builtins::{PyDictRef, PyStrRef}, - class::PyClassImpl, - convert::ToPyObject, - function::{ArgCallable, OptionalArg}, - import::import_source, + builtins::{PyDictRef, PyStrRef}, class::PyClassImpl, convert::ToPyObject, function::{ArgCallable, OptionalArg}, import::import_source, types::Constructor, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine }; use wasm_bindgen::{JsCast, prelude::*}; use wasm_bindgen_futures::JsFuture; @@ -168,6 +164,47 @@ mod _browser { #[pyclass] impl Document { + #[pygetset] + fn body(&self, vm: &VirtualMachine) -> PyResult { + let body = self + .doc + .body() + .map(|elem| Element { elem }) + .to_pyobject(vm); + Ok(body) + } + + #[pygetset] + fn cookie(&self, vm: &VirtualMachine) -> PyResult { + let cookie = self + .doc + .cookie() + .map_err(|err| convert::js_py_typeerror(vm, err))?; + Ok(vm.ctx.new_str(cookie).into()) + } + + #[pymethod] + fn get_element_by_id(&self, id: PyStrRef, vm: &VirtualMachine) -> PyResult { + let elem = self + .doc + .get_element_by_id(id.as_str()) + .map_err(|err| convert::js_py_typeerror(vm, err))? + .map(|elem| Element { elem }) + .to_pyobject(vm); + Ok(elem) + } + + #[pygetset] + fn head(&self, vm: &VirtualMachine) -> PyResult { + let head = self + .doc + .head() + .map_err(|err| convert::js_py_typeerror(vm, err))? + .map(|elem| Element { elem }) + .to_pyobject(vm); + Ok(head) + } + #[pymethod] fn query(&self, query: PyStrRef, vm: &VirtualMachine) -> PyResult { let elem = self @@ -178,6 +215,22 @@ mod _browser { .to_pyobject(vm); Ok(elem) } + + #[pygetset] + fn title(&self, vm: &VirtualMachine) -> PyResult { + let title = self + .doc + .title() + .map_err(|err| convert::js_py_typeerror(vm, err))?; + Ok(vm.ctx.new_str(title).into()) + } + + #[pygetset(setter)] + fn set_title(&self, title: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + self.doc + .set_title(title.as_str()) + .map_err(|err| convert::js_py_typeerror(vm, err)) + } } #[pyattr] @@ -257,7 +310,17 @@ mod _browser { } } +fn init_browser_module(vm: &VirtualMachine) { + let module = make_module(vm); + + extend_module!(vm, &module, { + "window" => js_module::PyJsValue::new(wasm_builtins::window()).into_ref(&vm.ctx), + }); + + module +} + pub fn setup_browser_module(vm: &mut VirtualMachine) { - vm.add_native_module("_browser".to_owned(), Box::new(make_module)); + vm.add_native_module("_browser".to_owned(), Box::new(init_browser_module)); vm.add_frozen(py_freeze!(dir = "Lib")); } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index bbd895c989..d4185af300 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -25,19 +25,6 @@ pub(crate) struct StoredVirtualMachine { held_objects: RefCell>, } -#[pymodule] -mod _window {} - -fn init_window_module(vm: &VirtualMachine) -> PyRef { - let module = _window::make_module(vm); - - extend_module!(vm, &module, { - "window" => js_module::PyJsValue::new(wasm_builtins::window()).into_ref(&vm.ctx), - }); - - module -} - impl StoredVirtualMachine { fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut scope = None; @@ -54,7 +41,6 @@ impl StoredVirtualMachine { js_module::setup_js_module(vm); if inject_browser_module { - vm.add_native_module("_window".to_owned(), Box::new(init_window_module)); setup_browser_module(vm); } From 9bc6964046975f1ba679a0f97674899f9026fadc Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 16:47:18 -0700 Subject: [PATCH 5/9] update wasm package description --- wasm/lib/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 0e35292a2c..65f0ee5e0d 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpython_wasm" -description = "A Python-3 (CPython >= 3.5.0) Interpreter written in Rust, compiled to WASM" +description = "A Python-3 (CPython >= 3.13) Interpreter written in Rust, compiled to WASM" version.workspace = true authors.workspace = true edition.workspace = true From 7fdd193dbd4ca4ebcb41144dd3a7cd7cf785e4b5 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 18:20:45 -0700 Subject: [PATCH 6/9] restyle run button --- wasm/demo/src/style.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wasm/demo/src/style.css b/wasm/demo/src/style.css index f9892745ac..53f066cb52 100644 --- a/wasm/demo/src/style.css +++ b/wasm/demo/src/style.css @@ -17,7 +17,11 @@ textarea { #run-btn { width: 6em; height: 2em; - font-size: 24px; + font-size: 20px; + border-radius: 8px; + margin: 8px; + background-color: #00913a; + color: white; } #error { From b121443c9f652e99be7b654d4cf04518ff9c048b Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 18:21:03 -0700 Subject: [PATCH 7/9] update codemirror/lang-python --- wasm/demo/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/demo/package.json b/wasm/demo/package.json index 487f46fbc1..bfd0fc0912 100644 --- a/wasm/demo/package.json +++ b/wasm/demo/package.json @@ -4,7 +4,7 @@ "description": "Bindings to the RustPython library for WebAssembly", "main": "index.js", "dependencies": { - "@codemirror/lang-python": "^6.1.6", + "@codemirror/lang-python": "^6.2.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.3.0", "codemirror": "^6.0.1", From 026eaf8f9d5e73c0080366b806eab35978c7ad4a Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 18:21:29 -0700 Subject: [PATCH 8/9] add HTMLElement --- wasm/lib/Cargo.toml | 5 +-- wasm/lib/src/browser_module.rs | 60 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 65f0ee5e0d..c28a689ebf 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -39,11 +39,12 @@ web-sys = { version = "0.3", features = [ "console", "Document", "Element", - "Window", "Headers", + "HtmlElement", "Request", "RequestInit", - "Response" + "Response", + "Window", ] } [package.metadata.wasm-pack.profile.release] diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f2e510823c..089d572aa6 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -2,6 +2,9 @@ use rustpython_vm::VirtualMachine; pub(crate) use _browser::make_module; use crate::wasm_builtins::window; +use rustpython_vm::PyRef; +use rustpython_vm::builtins::PyModule; +use rustpython_vm::PyPayload; #[pymodule] mod _browser { @@ -169,42 +172,21 @@ mod _browser { let body = self .doc .body() - .map(|elem| Element { elem }) + .map(|elem| HTMLElement { elem }) .to_pyobject(vm); Ok(body) } - #[pygetset] - fn cookie(&self, vm: &VirtualMachine) -> PyResult { - let cookie = self - .doc - .cookie() - .map_err(|err| convert::js_py_typeerror(vm, err))?; - Ok(vm.ctx.new_str(cookie).into()) - } - #[pymethod] fn get_element_by_id(&self, id: PyStrRef, vm: &VirtualMachine) -> PyResult { let elem = self .doc .get_element_by_id(id.as_str()) - .map_err(|err| convert::js_py_typeerror(vm, err))? .map(|elem| Element { elem }) .to_pyobject(vm); Ok(elem) } - #[pygetset] - fn head(&self, vm: &VirtualMachine) -> PyResult { - let head = self - .doc - .head() - .map_err(|err| convert::js_py_typeerror(vm, err))? - .map(|elem| Element { elem }) - .to_pyobject(vm); - Ok(head) - } - #[pymethod] fn query(&self, query: PyStrRef, vm: &VirtualMachine) -> PyResult { let elem = self @@ -220,16 +202,13 @@ mod _browser { fn title(&self, vm: &VirtualMachine) -> PyResult { let title = self .doc - .title() - .map_err(|err| convert::js_py_typeerror(vm, err))?; + .title(); Ok(vm.ctx.new_str(title).into()) } #[pygetset(setter)] - fn set_title(&self, title: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { - self.doc - .set_title(title.as_str()) - .map_err(|err| convert::js_py_typeerror(vm, err)) + fn set_title(&self, title: PyStrRef) { + self.doc.set_title(title.as_str()); } } @@ -274,6 +253,27 @@ mod _browser { } } + #[pyattr] + #[pyclass(module = "browser", name)] + #[derive(Debug, PyPayload)] + struct HTMLElement { + elem: web_sys::HtmlElement, + } + + #[pyclass] + impl HTMLElement { + #[pygetset] + fn title(&self, vm: &VirtualMachine) -> PyResult { + let title = self.elem.title(); + Ok(vm.ctx.new_str(title).into()) + } + + #[pygetset(setter)] + fn set_title(&self, title: PyStrRef) { + self.elem.set_title(title.as_str()); + } + } + #[pyfunction] fn load_module(module: PyStrRef, path: PyStrRef, vm: &VirtualMachine) -> PyResult { let weak_vm = weak_vm(vm); @@ -310,11 +310,11 @@ mod _browser { } } -fn init_browser_module(vm: &VirtualMachine) { +fn init_browser_module(vm: &VirtualMachine) -> PyRef { let module = make_module(vm); extend_module!(vm, &module, { - "window" => js_module::PyJsValue::new(wasm_builtins::window()).into_ref(&vm.ctx), + "window" => crate::js_module::PyJsValue::new(crate::wasm_builtins::window()).into_ref(&vm.ctx), }); module From 4e2aedcb4ede3bf474b3b50eb13f9451cfba322e Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 28 Apr 2025 18:21:43 -0700 Subject: [PATCH 9/9] minor fixes --- wasm/lib/Lib/browser/util.py | 2 +- wasm/lib/Lib/browser/window.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/lib/Lib/browser/util.py b/wasm/lib/Lib/browser/util.py index e274598cbd..1bb21d7c4c 100644 --- a/wasm/lib/Lib/browser/util.py +++ b/wasm/lib/Lib/browser/util.py @@ -1,4 +1,4 @@ -from _window import window # type: ignore +from _browser import window # type: ignore jsstr = window.new_from_str jsclosure = window.new_closure diff --git a/wasm/lib/Lib/browser/window.py b/wasm/lib/Lib/browser/window.py index d43a06f933..31b3f67bb1 100644 --- a/wasm/lib/Lib/browser/window.py +++ b/wasm/lib/Lib/browser/window.py @@ -1,4 +1,4 @@ -from _window import window as Window # type: ignore +from browser import window as Window # type: ignore from .util import jsint, jsstr, UNDEFINED __all__ = [ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy