Skip to content

Commit c933a6b

Browse files
hoodmaneambvmerwok
authored
gh-124621: Emscripten: Support pyrepl in browser (GH-136931)
Basic support for pyrepl in Emscripten. Limitations: * requires JSPI * no signal handling implemented As followup work, it would be nice to implement a webworker variant for when JSPI is not available and proper signal handling. Because it requires JSPI, it doesn't work in Safari. Firefox requires setting an experimental flag. All the Chromiums have full support since May. Until we make it work without JSPI, let's keep the original web_example around. Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Éric <merwok@netwok.org>
1 parent 22c8658 commit c933a6b

File tree

13 files changed

+504
-39
lines changed

13 files changed

+504
-39
lines changed

Lib/_pyrepl/trace.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import sys
45

56
# types
67
if False:
@@ -12,10 +13,22 @@
1213
trace_file = open(trace_filename, "a")
1314

1415

15-
def trace(line: str, *k: object, **kw: object) -> None:
16-
if trace_file is None:
17-
return
18-
if k or kw:
19-
line = line.format(*k, **kw)
20-
trace_file.write(line + "\n")
21-
trace_file.flush()
16+
17+
if sys.platform == "emscripten":
18+
from posix import _emscripten_log
19+
20+
def trace(line: str, *k: object, **kw: object) -> None:
21+
if "PYREPL_TRACE" not in os.environ:
22+
return
23+
if k or kw:
24+
line = line.format(*k, **kw)
25+
_emscripten_log(line)
26+
27+
else:
28+
def trace(line: str, *k: object, **kw: object) -> None:
29+
if trace_file is None:
30+
return
31+
if k or kw:
32+
line = line.format(*k, **kw)
33+
trace_file.write(line + "\n")
34+
trace_file.flush()

Lib/test/test_fcntl.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import sys
99
import unittest
1010
from test.support import (
11-
cpython_only, get_pagesize, is_apple, requires_subprocess, verbose
11+
cpython_only, get_pagesize, is_apple, requires_subprocess, verbose, is_emscripten
1212
)
1313
from test.support.import_helper import import_module
1414
from test.support.os_helper import TESTFN, unlink, make_bad_fd
@@ -211,6 +211,7 @@ def test_fcntl_f_getpath(self):
211211
@unittest.skipUnless(
212212
hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ"),
213213
"F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms.")
214+
@unittest.skipIf(is_emscripten, "Emscripten pipefs doesn't support these")
214215
def test_fcntl_f_pipesize(self):
215216
test_pipe_r, test_pipe_w = os.pipe()
216217
try:
@@ -265,12 +266,14 @@ def _check_fcntl_not_mutate_len(self, nbytes=None):
265266
@unittest.skipUnless(
266267
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
267268
"requires F_SETOWN_EX and F_GETOWN_EX")
269+
@unittest.skipIf(is_emscripten, "Emscripten doesn't actually support these")
268270
def test_fcntl_small_buffer(self):
269271
self._check_fcntl_not_mutate_len()
270272

271273
@unittest.skipUnless(
272274
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
273275
"requires F_SETOWN_EX and F_GETOWN_EX")
276+
@unittest.skipIf(is_emscripten, "Emscripten doesn't actually support these")
274277
def test_fcntl_large_buffer(self):
275278
self._check_fcntl_not_mutate_len(2024)
276279

Makefile.pre.in

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \
804804
python-config checksharedmods
805805

806806
.PHONY: build_emscripten
807-
build_emscripten: build_wasm web_example
807+
build_emscripten: build_wasm web_example web_example_pyrepl_jspi
808808

809809
# Check that the source is clean when building out of source.
810810
.PHONY: check-clean-src
@@ -1095,26 +1095,28 @@ $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS)
10951095

10961096
# wasm32-emscripten browser web example
10971097

1098-
WEBEX_DIR=$(srcdir)/Tools/wasm/emscripten/web_example/
1098+
EMSCRIPTEN_DIR=$(srcdir)/Tools/wasm/emscripten
1099+
WEBEX_DIR=$(EMSCRIPTEN_DIR)/web_example/
1100+
1101+
ZIP_STDLIB=python$(VERSION)$(ABI_THREAD).zip
1102+
$(ZIP_STDLIB): $(srcdir)/Lib/*.py $(srcdir)/Lib/*/*.py \
1103+
$(EMSCRIPTEN_DIR)/wasm_assets.py \
1104+
Makefile pybuilddir.txt Modules/Setup.local
1105+
$(PYTHON_FOR_BUILD) $(EMSCRIPTEN_DIR)/wasm_assets.py \
1106+
--buildroot . --prefix $(prefix) -o $@
1107+
10991108
web_example/index.html: $(WEBEX_DIR)/index.html
11001109
@mkdir -p web_example
11011110
@cp $< $@
11021111

1103-
web_example/python.worker.mjs: $(WEBEX_DIR)/python.worker.mjs
1112+
web_example/server.py: $(WEBEX_DIR)/server.py
11041113
@mkdir -p web_example
11051114
@cp $< $@
11061115

1107-
web_example/server.py: $(WEBEX_DIR)/server.py
1116+
web_example/$(ZIP_STDLIB): $(ZIP_STDLIB)
11081117
@mkdir -p web_example
11091118
@cp $< $@
11101119

1111-
WEB_STDLIB=web_example/python$(VERSION)$(ABI_THREAD).zip
1112-
$(WEB_STDLIB): $(srcdir)/Lib/*.py $(srcdir)/Lib/*/*.py \
1113-
$(WEBEX_DIR)/wasm_assets.py \
1114-
Makefile pybuilddir.txt Modules/Setup.local
1115-
$(PYTHON_FOR_BUILD) $(WEBEX_DIR)/wasm_assets.py \
1116-
--buildroot . --prefix $(prefix) -o $@
1117-
11181120
web_example/python.mjs web_example/python.wasm: $(BUILDPYTHON)
11191121
@if test $(HOST_GNU_TYPE) != 'wasm32-unknown-emscripten' ; then \
11201122
echo "Can only build web_example when target is Emscripten" ;\
@@ -1124,7 +1126,35 @@ web_example/python.mjs web_example/python.wasm: $(BUILDPYTHON)
11241126
cp python.wasm web_example/python.wasm
11251127

11261128
.PHONY: web_example
1127-
web_example: web_example/python.mjs web_example/python.worker.mjs web_example/index.html web_example/server.py $(WEB_STDLIB)
1129+
web_example: web_example/python.mjs web_example/index.html web_example/server.py web_example/$(ZIP_STDLIB)
1130+
1131+
WEBEX2=web_example_pyrepl_jspi
1132+
WEBEX2_DIR=$(EMSCRIPTEN_DIR)/$(WEBEX2)/
1133+
1134+
$(WEBEX2)/python.mjs $(WEBEX2)/python.wasm: $(BUILDPYTHON)
1135+
@if test $(HOST_GNU_TYPE) != 'wasm32-unknown-emscripten' ; then \
1136+
echo "Can only build web_example when target is Emscripten" ;\
1137+
exit 1 ;\
1138+
fi
1139+
@mkdir -p $(WEBEX2)
1140+
@cp python.mjs $(WEBEX2)/python.mjs
1141+
@cp python.wasm $(WEBEX2)/python.wasm
1142+
1143+
$(WEBEX2)/index.html: $(WEBEX2_DIR)/index.html
1144+
@mkdir -p $(WEBEX2)
1145+
@cp $< $@
1146+
1147+
$(WEBEX2)/src.mjs: $(WEBEX2_DIR)/src.mjs
1148+
@mkdir -p $(WEBEX2)
1149+
@cp $< $@
1150+
1151+
$(WEBEX2)/$(ZIP_STDLIB): $(ZIP_STDLIB)
1152+
@mkdir -p $(WEBEX2)
1153+
@cp $< $@
1154+
1155+
.PHONY: web_example_pyrepl_jspi
1156+
web_example_pyrepl_jspi: $(WEBEX2)/python.mjs $(WEBEX2)/index.html $(WEBEX2)/src.mjs $(WEBEX2)/$(ZIP_STDLIB)
1157+
11281158

11291159
############################################################################
11301160
# Header files
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyrepl now works in Emscripten.

Modules/clinic/posixmodule.c.h

Lines changed: 79 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16971,6 +16971,25 @@ os__emscripten_debugger_impl(PyObject *module)
1697116971
emscripten_debugger();
1697216972
Py_RETURN_NONE;
1697316973
}
16974+
16975+
EM_JS(void, emscripten_log_impl_js, (const char* arg), {
16976+
console.warn(UTF8ToString(arg));
16977+
});
16978+
16979+
/*[clinic input]
16980+
os._emscripten_log
16981+
arg: str
16982+
16983+
Log something to the JS console. Emscripten only.
16984+
[clinic start generated code]*/
16985+
16986+
static PyObject *
16987+
os__emscripten_log_impl(PyObject *module, const char *arg)
16988+
/*[clinic end generated code: output=9749e5e293c42784 input=350aa1f70bc1e905]*/
16989+
{
16990+
emscripten_log_impl_js(arg);
16991+
Py_RETURN_NONE;
16992+
}
1697416993
#endif /* __EMSCRIPTEN__ */
1697516994

1697616995

@@ -17190,6 +17209,7 @@ static PyMethodDef posix_methods[] = {
1719017209
OS__IS_INPUTHOOK_INSTALLED_METHODDEF
1719117210
OS__CREATE_ENVIRON_METHODDEF
1719217211
OS__EMSCRIPTEN_DEBUGGER_METHODDEF
17212+
OS__EMSCRIPTEN_LOG_METHODDEF
1719317213
{NULL, NULL} /* Sentinel */
1719417214
};
1719517215

0 commit comments

Comments
 (0)
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