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 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", 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 { diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 0e35292a2c..c28a689ebf 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 @@ -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/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() 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..1bb21d7c4c --- /dev/null +++ b/wasm/lib/Lib/browser/util.py @@ -0,0 +1,11 @@ +from _browser 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..31b3f67bb1 --- /dev/null +++ b/wasm/lib/Lib/browser/window.py @@ -0,0 +1,99 @@ +from browser 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() diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index f8d1b2ebc3..089d572aa6 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,18 +1,17 @@ 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 { 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 +167,26 @@ mod _browser { #[pyclass] impl Document { + #[pygetset] + fn body(&self, vm: &VirtualMachine) -> PyResult { + let body = self + .doc + .body() + .map(|elem| HTMLElement { elem }) + .to_pyobject(vm); + Ok(body) + } + + #[pymethod] + fn get_element_by_id(&self, id: PyStrRef, vm: &VirtualMachine) -> PyResult { + let elem = self + .doc + .get_element_by_id(id.as_str()) + .map(|elem| Element { elem }) + .to_pyobject(vm); + Ok(elem) + } + #[pymethod] fn query(&self, query: PyStrRef, vm: &VirtualMachine) -> PyResult { let elem = self @@ -178,6 +197,19 @@ mod _browser { .to_pyobject(vm); Ok(elem) } + + #[pygetset] + fn title(&self, vm: &VirtualMachine) -> PyResult { + let title = self + .doc + .title(); + Ok(vm.ctx.new_str(title).into()) + } + + #[pygetset(setter)] + fn set_title(&self, title: PyStrRef) { + self.doc.set_title(title.as_str()); + } } #[pyattr] @@ -221,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); @@ -257,7 +310,17 @@ mod _browser { } } +fn init_browser_module(vm: &VirtualMachine) -> PyRef { + let module = make_module(vm); + + extend_module!(vm, &module, { + "window" => crate::js_module::PyJsValue::new(crate::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); } 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