Skip to content
Merged
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
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.1.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