Skip to content

Commit 924f62b

Browse files
authored
Python: Update create_vendor_zip.py to support multiple packages (#5326)
And also to work with newer versions of pywrangler
1 parent 84df6c8 commit 924f62b

File tree

1 file changed

+41
-24
lines changed

1 file changed

+41
-24
lines changed

src/pyodide/create_vendor_zip.py

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,55 @@
1313
import sys
1414
import zipfile
1515
from pathlib import Path
16+
from typing import Literal
1617

1718
from tool_utils import hexdigest, run
1819

1920
PYPROJECT_TEMPLATE = """[project]
2021
name = "vendor-test"
2122
version = "0.1.0"
2223
requires-python = ">=3.12"
23-
dependencies = [
24-
"{package_name}"
25-
]
24+
dependencies = {dependencies}
2625
2726
[dependency-groups]
28-
dev = ["workers-py"]
27+
dev = ["workers-py>=1.6"]
2928
"""
3029

30+
WRANGLER_TOML = """
31+
name = "hello-python-bindings"
32+
main = "src/entry.py"
33+
compatibility_flags = {compat_flags}
34+
compatibility_date = "2025-08-14"
35+
"""
36+
37+
type PyVer = Literal["3.12", "3.13"]
3138

32-
def create_pyproject_toml(package_name: str, target_dir: Path) -> Path:
39+
40+
def create_pyproject_toml(package_names: list[str], target_dir: Path) -> Path:
3341
"""Create a pyproject.toml file with the specified package as a dependency."""
34-
pyproject_content = PYPROJECT_TEMPLATE.format(package_name=package_name)
42+
pyproject_content = PYPROJECT_TEMPLATE.format(dependencies=repr(package_names))
3543
pyproject_path = target_dir / "pyproject.toml"
3644
pyproject_path.write_text(pyproject_content)
37-
# Also create a wrangler file as otherwise pywrangler won't run
38-
wrangler_path = target_dir / "wrangler.toml"
39-
wrangler_path.write_text("\n")
4045
return pyproject_path
4146

4247

43-
def run_pywrangler_sync(work_dir: Path, python: str | None) -> Path:
48+
def create_wrangler_toml(target_dir: Path, python: PyVer):
49+
compat_flags = ["python_workers"]
50+
match python:
51+
case "3.13":
52+
compat_flags.append("python_workers_20250116")
53+
54+
(target_dir / "wrangler.toml").write_text(
55+
WRANGLER_TOML.format(compat_flags=repr(compat_flags))
56+
)
57+
58+
59+
def run_pywrangler_sync(work_dir: Path) -> Path:
4460
"""Run `uv run pywrangler sync` in the specified directory."""
4561
env = os.environ.copy()
4662
env["_PYODIDE_EXTRA_MOUNTS"] = str(work_dir)
47-
if python:
48-
env["_PYWRANGLER_PYTHON_VERSION"] = python
4963
# TODO: Make pywrangler understand how to use Python 3.13 correctly and
5064
# remove these extra commands
51-
run(["uv", "venv"], cwd=work_dir, env=env)
5265
run(["uv", "run", "pywrangler", "sync"], cwd=work_dir, env=env)
5366
python_modules_dir = work_dir / "python_modules"
5467
if not python_modules_dir.exists():
@@ -57,7 +70,7 @@ def run_pywrangler_sync(work_dir: Path, python: str | None) -> Path:
5770
return python_modules_dir
5871

5972

60-
def create_zip_archive(source_dir: Path, package_name: str, output_dir: Path) -> bool:
73+
def create_zip_archive(source_dir: Path, output_dir: Path) -> bool:
6174
"""Create a zip archive of the python_modules directory.
6275
6376
Return value indicates whether the archive includes any binary modules (.so
@@ -78,10 +91,11 @@ def create_zip_archive(source_dir: Path, package_name: str, output_dir: Path) ->
7891
return native
7992

8093

81-
def vendor_package(package_name: str, python: str) -> tuple[Path, bool]:
94+
def vendor_package(package_names: list[str], python: PyVer) -> tuple[Path, bool]:
8295
"""Main function to vendor a Python package."""
8396
tmp_dir = Path("/tmp")
84-
work_dir = tmp_dir / f"vendor-{package_name}"
97+
vendor_name = "-".join(package_names)
98+
work_dir = tmp_dir / f"vendor-{vendor_name}"
8599

86100
# Clean up any existing work directory
87101
if work_dir.exists():
@@ -91,21 +105,22 @@ def vendor_package(package_name: str, python: str) -> tuple[Path, bool]:
91105
try:
92106
# Create pyproject.toml
93107
print(f"Creating pyproject.toml in {work_dir}")
94-
create_pyproject_toml(package_name, work_dir)
108+
create_pyproject_toml(package_names, work_dir)
109+
create_wrangler_toml(work_dir, python)
95110

96111
# Run pywrangler sync
97112
print("Running uv run pywrangler sync...")
98-
python_modules_dir = run_pywrangler_sync(work_dir, python)
113+
python_modules_dir = run_pywrangler_sync(work_dir)
99114

100115
# Create zip archive
101116
print("Creating zip archive...")
102-
native = create_zip_archive(python_modules_dir, package_name, tmp_dir)
117+
native = create_zip_archive(python_modules_dir, tmp_dir)
103118
py = f"-{python}" if native else ""
104-
name = f"{package_name}{py}-vendored-for-ew-testing.zip"
105-
zip_path = tmp_dir / name
119+
zip_name = f"{vendor_name}{py}-vendored-for-ew-testing.zip"
120+
zip_path = tmp_dir / zip_name
106121
shutil.move(tmp_dir / "tmp.zip", zip_path)
107122
except Exception as e:
108-
print(f"Error vendoring package {package_name}: {e}")
123+
print(f"Error vendoring packages {package_names!r}: {e}")
109124
sys.exit(1)
110125
else:
111126
print(f"Successfully created: {zip_path}")
@@ -125,7 +140,9 @@ def main() -> int:
125140
parser = argparse.ArgumentParser(
126141
description="Create a zip file of a vendored Python package's source files for vendored_py_wd_test."
127142
)
128-
parser.add_argument("package_name", help="Name of the Python package to vendor")
143+
parser.add_argument(
144+
"package_name", help="Name of the Python package to vendor", nargs="*"
145+
)
129146
parser.add_argument("-p", "--python", help="Name of the Python version to use")
130147

131148
args = parser.parse_args()
@@ -143,7 +160,7 @@ def main() -> int:
143160
i1 = " " * 12
144161
i2 = " " * 16
145162
print(i1 + "{")
146-
print(i2 + f'"name": "{args.package_name}",')
163+
print(i2 + f'"name": "{"-".join(args.package_name)}",')
147164
print(i2 + f'"abi": "{abi}",')
148165
print(i2 + f'"sha256": "{hexdigest(zip_path)}",')
149166
print(i1 + "},")

0 commit comments

Comments
 (0)