Skip to content

Browser refactor #5754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion wasm/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion wasm/demo/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 4 additions & 3 deletions wasm/lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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]
Expand Down
76 changes: 0 additions & 76 deletions wasm/lib/Lib/browser.py

This file was deleted.

34 changes: 34 additions & 0 deletions wasm/lib/Lib/browser/__init__.py
Original file line number Diff line number Diff line change
@@ -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",
]


11 changes: 11 additions & 0 deletions wasm/lib/Lib/browser/util.py
Original file line number Diff line number Diff line change
@@ -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()
99 changes: 99 additions & 0 deletions wasm/lib/Lib/browser/window.py
Original file line number Diff line number Diff line change
@@ -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()
77 changes: 70 additions & 7 deletions wasm/lib/src/browser_module.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -257,7 +310,17 @@ mod _browser {
}
}

fn init_browser_module(vm: &VirtualMachine) -> PyRef<PyModule> {
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"));
}
Loading
Loading