Skip to content
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ repos:
name: Run Ruff (format) on Tools/build/check_warnings.py
args: [--check, --config=Tools/build/.ruff.toml]
files: ^Tools/build/check_warnings.py
- id: ruff-format
name: Run Ruff (format) on Tools/wasm/
args: [--check, --config=Tools/wasm/.ruff.toml]
files: ^Tools/wasm/

- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.9.0
Expand Down
28 changes: 28 additions & 0 deletions Tools/wasm/.ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
extend = "../../.ruff.toml" # Inherit the project-wide settings

[format]
preview = true
docstring-code-format = true

[lint]
select = [
"C4", # flake8-comprehensions
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"PGH", # pygrep-hooks
"PT", # flake8-pytest-style
"PYI", # flake8-pyi
"RUF100", # Ban unused `# noqa` comments
"UP", # pyupgrade
"W", # pycodestyle
"YTT", # flake8-2020
]
ignore = [
"E501", # Line too long
"F541", # f-string without any placeholders
"PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple`
"PYI025", # Use `from collections.abc import Set as AbstractSet`
]
70 changes: 47 additions & 23 deletions Tools/wasm/emscripten/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix"

LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local"
LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode("utf-8")
LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode(
"utf-8"
)


def updated_env(updates={}):
Expand All @@ -45,7 +47,9 @@ def updated_env(updates={}):
# https://reproducible-builds.org/docs/source-date-epoch/
git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"]
try:
epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip()
epoch = subprocess.check_output(
git_epoch_cmd, encoding="utf-8"
).strip()
env_defaults["SOURCE_DATE_EPOCH"] = epoch
except subprocess.CalledProcessError:
pass # Might be building from a tarball.
Expand Down Expand Up @@ -79,7 +83,11 @@ def wrapper(context):
terminal_width = 80
print("⎯" * terminal_width)
print("📁", working_dir)
if clean_ok and getattr(context, "clean", False) and working_dir.exists():
if (
clean_ok
and getattr(context, "clean", False)
and working_dir.exists()
):
print("🚮 Deleting directory (--clean)...")
shutil.rmtree(working_dir)

Expand Down Expand Up @@ -128,7 +136,9 @@ def build_python_path():
if not binary.is_file():
binary = binary.with_suffix(".exe")
if not binary.is_file():
raise FileNotFoundError("Unable to find `python(.exe)` in " f"{NATIVE_BUILD_DIR}")
raise FileNotFoundError(
f"Unable to find `python(.exe)` in {NATIVE_BUILD_DIR}"
)

return binary

Expand Down Expand Up @@ -158,7 +168,8 @@ def make_build_python(context, working_dir):
cmd = [
binary,
"-c",
"import sys; " "print(f'{sys.version_info.major}.{sys.version_info.minor}')",
"import sys; "
"print(f'{sys.version_info.major}.{sys.version_info.minor}')",
]
version = subprocess.check_output(cmd, encoding="utf-8").strip()

Expand All @@ -173,7 +184,9 @@ def check_shasum(file: str, expected_shasum: str):


def download_and_unpack(working_dir: Path, url: str, expected_shasum: str):
with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete_on_close=False) as tmp_file:
with tempfile.NamedTemporaryFile(
suffix=".tar.gz", delete_on_close=False
) as tmp_file:
with urlopen(url) as response:
shutil.copyfileobj(response, tmp_file)
tmp_file.close()
Expand All @@ -186,7 +199,11 @@ def make_emscripten_libffi(context, working_dir):
ver = "3.4.6"
libffi_dir = working_dir / f"libffi-{ver}"
shutil.rmtree(libffi_dir, ignore_errors=True)
download_and_unpack(working_dir, f"https://github.com/libffi/libffi/releases/download/v{ver}/libffi-{ver}.tar.gz", "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e")
download_and_unpack(
working_dir,
f"https://github.com/libffi/libffi/releases/download/v{ver}/libffi-{ver}.tar.gz",
"b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e",
)
call(
[EMSCRIPTEN_DIR / "make_libffi.sh"],
env=updated_env({"PREFIX": PREFIX_DIR}),
Expand All @@ -200,7 +217,11 @@ def make_mpdec(context, working_dir):
ver = "4.0.1"
mpdec_dir = working_dir / f"mpdecimal-{ver}"
shutil.rmtree(mpdec_dir, ignore_errors=True)
download_and_unpack(working_dir, f"https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{ver}.tar.gz", "96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8")
download_and_unpack(
working_dir,
f"https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{ver}.tar.gz",
"96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8",
)
call(
[
"emconfigure",
Expand All @@ -214,10 +235,7 @@ def make_mpdec(context, working_dir):
quiet=context.quiet,
)
call(
[
"make",
"install"
],
["make", "install"],
cwd=mpdec_dir,
quiet=context.quiet,
)
Expand All @@ -226,17 +244,15 @@ def make_mpdec(context, working_dir):
@subdir(HOST_DIR, clean_ok=True)
def configure_emscripten_python(context, working_dir):
"""Configure the emscripten/host build."""
config_site = os.fsdecode(
EMSCRIPTEN_DIR / "config.site-wasm32-emscripten"
)
config_site = os.fsdecode(EMSCRIPTEN_DIR / "config.site-wasm32-emscripten")

emscripten_build_dir = working_dir.relative_to(CHECKOUT)

python_build_dir = NATIVE_BUILD_DIR / "build"
lib_dirs = list(python_build_dir.glob("lib.*"))
assert (
len(lib_dirs) == 1
), f"Expected a single lib.* directory in {python_build_dir}"
assert len(lib_dirs) == 1, (
f"Expected a single lib.* directory in {python_build_dir}"
)
lib_dir = os.fsdecode(lib_dirs[0])
pydebug = lib_dir.endswith("-pydebug")
python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1]
Expand Down Expand Up @@ -290,7 +306,9 @@ def configure_emscripten_python(context, working_dir):
quiet=context.quiet,
)

shutil.copy(EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs")
shutil.copy(
EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs"
)

node_entry = working_dir / "node_entry.mjs"
exec_script = working_dir / "python.sh"
Expand Down Expand Up @@ -383,13 +401,15 @@ def main():
subcommands = parser.add_subparsers(dest="subcommand")
build = subcommands.add_parser("build", help="Build everything")
configure_build = subcommands.add_parser(
"configure-build-python", help="Run `configure` for the " "build Python"
"configure-build-python", help="Run `configure` for the build Python"
)
make_mpdec_cmd = subcommands.add_parser(
"make-mpdec", help="Clone mpdec repo, configure and build it for emscripten"
"make-mpdec",
help="Clone mpdec repo, configure and build it for emscripten",
)
make_libffi_cmd = subcommands.add_parser(
"make-libffi", help="Clone libffi repo, configure and build it for emscripten"
"make-libffi",
help="Clone libffi repo, configure and build it for emscripten",
)
make_build = subcommands.add_parser(
"make-build-python", help="Run `make` for the build Python"
Expand Down Expand Up @@ -457,7 +477,11 @@ def main():

if not context.subcommand:
# No command provided, display help and exit
print("Expected one of", ", ".join(sorted(dispatch.keys())), file=sys.stderr)
print(
"Expected one of",
", ".join(sorted(dispatch.keys())),
file=sys.stderr,
)
parser.print_help(sys.stderr)
sys.exit(1)
dispatch[context.subcommand](context)
Expand Down
5 changes: 2 additions & 3 deletions Tools/wasm/emscripten/prepare_external_wasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
}});
"""


def prepare_wasm(input_file, output_file, function_name):
# Read the compiled WASM as binary and convert to hex
wasm_bytes = Path(input_file).read_bytes()
Expand All @@ -31,9 +32,7 @@ def prepare_wasm(input_file, output_file, function_name):
)
Path(output_file).write_text(js_content)

print(
f"Successfully compiled {input_file} and generated {output_file}"
)
print(f"Successfully compiled {input_file} and generated {output_file}")
return 0


Expand Down
4 changes: 3 additions & 1 deletion Tools/wasm/emscripten/wasm_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
WASM_STDLIB_ZIP = (
WASM_LIB / f"python{sys.version_info.major}{sys.version_info.minor}.zip"
)
WASM_STDLIB = WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}"
WASM_STDLIB = (
WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}"
)
WASM_DYNLOAD = WASM_STDLIB / "lib-dynload"


Expand Down
10 changes: 8 additions & 2 deletions Tools/wasm/emscripten/web_example/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
description="Start a local webserver with a Python terminal."
)
parser.add_argument(
"--port", type=int, default=8000, help="port for the http server to listen on"
"--port",
type=int,
default=8000,
help="port for the http server to listen on",
)
parser.add_argument(
"--bind", type=str, default="127.0.0.1", help="Bind address (empty for all)"
"--bind",
type=str,
default="127.0.0.1",
help="Bind address (empty for all)",
)


Expand Down
10 changes: 6 additions & 4 deletions Tools/wasm/wasi.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
if __name__ == "__main__":
if __name__ == "__main__":
import pathlib
import runpy
import sys

print("⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; "
"execute the `wasi/` directory instead (i.e. `python Tools/wasm/wasi`)\n",
file=sys.stderr)
print(
"⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; "
"execute the `wasi/` directory instead (i.e. `python Tools/wasm/wasi`)\n",
file=sys.stderr,
)

runpy.run_path(pathlib.Path(__file__).parent / "wasi", run_name="__main__")
Loading
Loading