Skip to content

Commit 54db7f9

Browse files
authored
Merge pull request #4 from cloudflare/tidy-python
Tidy up script.py and import_tests.py
2 parents 5b860de + 9f9d8d6 commit 54db7f9

File tree

2 files changed

+105
-74
lines changed

2 files changed

+105
-74
lines changed

packages/import_tests.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
from pathlib import Path
22

3-
from typing import List
4-
import yaml
3+
from pyodide_build.io import MetaConfig
54

6-
def gen(packages: List[str], packages_dir = Path("packages")):
5+
6+
def gen(packages: list[str], packages_dir: Path = Path("packages")) -> dict[str, list[str]]:
77
res = {}
88
for package in packages:
9-
if package == "test": continue
9+
if package == "test":
10+
continue
1011
try:
11-
with open(packages_dir / package / "meta.yaml", "r") as file:
12-
imports = set()
13-
meta = yaml.load(file, Loader=yaml.FullLoader)
14-
# add package -> top-level if it exists
15-
if "package" in meta:
16-
if "top-level" in meta["package"]:
17-
imports.update(meta["package"]["top-level"])
18-
# add test -> imports if it exists
19-
if "test" in meta:
20-
if "imports" in meta["test"]:
21-
imports.update(meta["test"]["imports"])
22-
23-
if len(imports) > 0:
24-
res[package] = sorted(list(imports))
12+
meta = MetaConfig.from_yaml(packages_dir / package / "meta.yaml")
2513
except FileNotFoundError:
2614
print(f"package {package}'s meta.yaml not found")
27-
return res
15+
16+
imports = set()
17+
# add package -> top-level if it exists
18+
imports.update(meta.package.top_level)
19+
imports.update(meta.test.imports)
20+
if imports:
21+
res[package] = sorted(list(imports))
22+
23+
return res

packages/script.py

Lines changed: 90 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
1-
import os
1+
import hashlib
22
import json
3-
from pathlib import Path
3+
import os
4+
import re
5+
import subprocess
6+
import sys
7+
import tarfile
48
import tempfile
59
import zipfile
6-
import tarfile
7-
import uuid
10+
from pathlib import Path
11+
from textwrap import dedent
12+
813
import boto3
914
import re
1015
import sys
1116
import hashlib
1217
import time
1318
import requests
14-
from datetime import datetime
1519

1620
import import_tests
1721

18-
def normalize(name):
22+
23+
def normalize(name: str) -> str:
1924
return re.sub(r"[-_.]+", "-", name).lower()
2025

26+
2127
# See setup.sh
2228
# prerequisite: emsdk, pyodide, packages -> pyodide/packages
2329

24-
def gen_bzl_config(tag, dist):
25-
bucket_url = "https://pyodide.edgeworker.net/python-package-bucket/" + tag + "/"
26-
github_url = "https://github.com/cloudflare/pyodide-build-scripts/releases/download/" + tag + "/"
30+
31+
def gen_bzl_config(tag: str, dist: Path) -> None:
32+
bucket_url = f"https://pyodide.edgeworker.net/python-package-bucket/{tag}/"
33+
github_url = (
34+
f"https://github.com/cloudflare/pyodide-build-scripts/releases/download/{tag}/"
35+
)
2736
lock_bytes = (dist / "pyodide-lock.json").read_bytes()
2837
lock_hash = hashlib.sha256(lock_bytes).hexdigest()
2938
zip_bytes = (dist / "pyodide_packages.tar.zip").read_bytes()
@@ -33,24 +42,36 @@ def gen_bzl_config(tag, dist):
3342

3443
with open(dist / "pyodide-lock.json", "r") as file:
3544
lock = json.load(file)
36-
packages = [package["name"] for package in lock["packages"].values()]
37-
imports_to_test = import_tests.gen(packages)
38-
39-
with open("pyodide_bucket.bzl", "w") as f:
40-
f.write("# Do not edit this file by hand. See docs/pyodide.md for info on how to generate it.\n")
41-
f.write("# These variables are factored out here because they are being shared by the WORKSPACE files in\n")
42-
f.write("# both edgeworker and workerd, as well as src/pyodide/BUILD.bazel\n")
43-
f.write("PYODIDE_PACKAGE_BUCKET_URL = \"" + bucket_url + "\"\n")
44-
f.write("PYODIDE_GITHUB_RELEASE_URL = \"" + github_url + "\"\n")
45-
f.write("PYODIDE_LOCK_SHA256 = \"" + lock_hash + "\"\n")
46-
f.write("PYODIDE_PACKAGES_TAR_ZIP_SHA256 = \"" + zip_hash + "\"\n")
47-
f.write("PYODIDE_ALL_WHEELS_ZIP_SHA256 = \"" + all_wheels_hash + "\"\n\n")
48-
f.write("# IMPORTANT: when updating this file in git, check the diff to make sure none of the imports below are being removed unexpectedly\n")
49-
f.write("PYODIDE_IMPORTS_TO_TEST = " + json.dumps(imports_to_test, indent=3, sort_keys=True) + "\n")
45+
packages = [package["name"] for package in lock["packages"].values()]
46+
imports_to_test = import_tests.gen(packages)
47+
48+
Path("pyodide_bucket.bzl").write_text(
49+
dedent(
50+
f"""
51+
# Do not edit this file by hand. See docs/pyodide.md for info on how to generate it.
52+
# These variables are factored out here because they are being shared by the WORKSPACE files in
53+
# both edgeworker and workerd, as well as src/pyodide/BUILD.bazel
54+
55+
PYODIDE_PACKAGE_BUCKET_URL = "{bucket_url}"
56+
PYODIDE_GITHUB_RELEASE_URL = "{github_url}"
57+
PYODIDE_LOCK_SHA256 = "{lock_hash}"
58+
PYODIDE_PACKAGES_TAR_ZIP_SHA256 = "{zip_hash}"
59+
PYODIDE_ALL_WHEELS_ZIP_SHA256 = "{all_wheels_hash}"
60+
61+
# IMPORTANT: when updating this file in git, check the diff to make sure none of the imports below are being removed unexpectedly
62+
63+
PYODIDE_IMPORTS_TO_TEST =
64+
"""
65+
).strip()
66+
+ " "
67+
+ json.dumps(imports_to_test, indent=3, sort_keys=True)
68+
+ "\n"
69+
)
70+
5071

5172
# creates a package bundle .tar.zip file to be bundled in with edgeworker
5273
# the resulting bundle is written to dist/pyodide_packages.tar.zip
53-
def make_bundle(tag, dist = Path("dist")):
74+
def make_bundle(tag: str, dist: Path = Path("dist")) -> None:
5475
with open(dist / "pyodide-lock.json", "r") as file:
5576
lock = json.load(file)
5677
with tempfile.TemporaryDirectory(delete=False) as t:
@@ -62,8 +83,8 @@ def make_bundle(tag, dist = Path("dist")):
6283
for package in lock["packages"].values():
6384
name = normalize(package["name"])
6485
print("untarring " + name)
65-
os.mkdir(tempdir / name)
66-
if name.endswith("-tests") or name == "test":
86+
(tempdir / name).mkdir()
87+
if name.endswith("-tests") or name == "test":
6788
continue
6889
file = dist / package["file_name"]
6990
with tarfile.open(file, "r:gz") as zip:
@@ -72,27 +93,37 @@ def make_bundle(tag, dist = Path("dist")):
7293
with tarfile.open(tempdir / "pyodide_packages.tar", "w") as tar:
7394
tar.add(tempdir, arcname="./")
7495
# create zip file in dist/ from tarfile
75-
with zipfile.ZipFile(dist / "pyodide_packages.tar.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
96+
with zipfile.ZipFile(
97+
dist / "pyodide_packages.tar.zip", "w", compression=zipfile.ZIP_DEFLATED
98+
) as zip:
7699
zip.write(tempdir / "pyodide_packages.tar", "pyodide_packages.tar")
77100
# create all_wheels.zip file for testing
78-
with zipfile.ZipFile(dist / "all_wheels.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
101+
with zipfile.ZipFile(
102+
dist / "all_wheels.zip", "w", compression=zipfile.ZIP_DEFLATED
103+
) as zip:
79104
for package in lock["packages"].values():
80105
file = dist / package["file_name"]
81106
zip.write(file, f"{package['file_name']}")
82-
107+
83108
gen_bzl_config(tag, dist)
84109

110+
85111
# uploads everything in dist to python-package-bucket at tag/...
86-
def upload_to_r2(tag, dist = Path("dist")):
112+
def upload_to_r2(tag: str, dist=Path("dist")) -> None:
87113
# upload to r2
88-
s3 = boto3.client("s3",
89-
endpoint_url = "https://" + os.environ.get("R2_ACCOUNT_ID") + ".r2.cloudflarestorage.com",
90-
aws_access_key_id = os.environ.get("R2_ACCESS_KEY_ID"),
91-
aws_secret_access_key = os.environ.get("R2_SECRET_ACCESS_KEY"),
92-
region_name="auto")
93-
114+
r2_account_id = os.environ["R2_ACCOUNT_ID"]
115+
r2_access_key = os.environ.get("R2_ACCESS_KEY_ID")
116+
r2_secret_access_key = os.environ.get("R2_SECRET_ACCESS_KEY")
117+
s3 = boto3.client(
118+
"s3",
119+
endpoint_url=f"https://{r2_account_id}.r2.cloudflarestorage.com",
120+
aws_access_key_id=r2_access_key,
121+
aws_secret_access_key=r2_secret_access_key,
122+
region_name="auto",
123+
)
124+
94125
files_remaining = []
95-
126+
96127
# upload entire dist directory to r2, excluding all_wheels.zip and pyodide_packages.tar.zip
97128
for root, dirs, files in os.walk(dist):
98129
for file in files:
@@ -101,30 +132,30 @@ def upload_to_r2(tag, dist = Path("dist")):
101132
path = Path(root) / file
102133
key = tag + "/" + str(path.relative_to(dist))
103134
files_remaining.append((path, key))
104-
135+
105136
# attempt to upload each file 5 times. If after 5 attempts the file is still not accessible at pyodide.edgeworker.net then give up
106137
ATTEMPTS = 5
107138
for i in range(ATTEMPTS):
108-
for (path, key) in files_remaining:
139+
for path, key in files_remaining:
109140
print(f"uploading {path} to {key}")
110141
s3.upload_file(str(path), "python-package-bucket", key)
111142

112143
new_files_remaining = []
113144

114145
time.sleep(10)
115146

116-
for (path, key) in files_remaining:
147+
for path, key in files_remaining:
117148
# Construct URL to fetch the uploaded file
118149
url = f"https://pyodide.edgeworker.net/python-package-bucket/{key}"
119150
print(f"Checking {url}")
120-
151+
121152
try:
122153
# Download the file content from the URL
123154
response = requests.get(url)
124155
response.raise_for_status() # Raise an exception if the status is not 200 OK
125-
156+
126157
# Read the local file content
127-
with open(path, 'rb') as f:
158+
with open(path, "rb") as f:
128159
local_content = f.read()
129160

130161
# Compare contents
@@ -143,19 +174,19 @@ def upload_to_r2(tag, dist = Path("dist")):
143174
break
144175

145176
if i != ATTEMPTS - 1:
146-
for (path, key) in files_remaining:
177+
for path, key in files_remaining:
147178
s3.delete_object(Bucket="python-package-bucket", Key=key)
148179

149180
if files_remaining:
150181
raise Exception("Failed to upload packages after 5 attempts: ", files_remaining)
151182

183+
152184
# converts all the .zip wheels into .tar.gz format (destructively)
153-
def convert_wheels_to_tar_gz(dist = Path("dist")):
185+
def convert_wheels_to_tar_gz(dist: Path = Path("dist")) -> None:
154186
with open(dist / "pyodide-lock.json", "r") as file:
155187
lock = json.load(file)
156-
188+
157189
for package in lock["packages"].values():
158-
name = normalize(package["name"])
159190
file = dist / package["file_name"]
160191
# check file ends with .zip or .whl
161192
if not (file.name.endswith(".zip") or file.name.endswith(".whl")):
@@ -169,16 +200,17 @@ def convert_wheels_to_tar_gz(dist = Path("dist")):
169200
# create tar.gz file from tempdir
170201
with tarfile.open(new_file, "w:gz") as tar:
171202
tar.add(tempdir, arcname="./")
172-
os.remove(file)
203+
file.unlink()
173204
package["file_name"] = new_file.name
174205
# update sha256 hash
175206
new_file_bytes = new_file.read_bytes()
176207
new_file_hash = hashlib.sha256(new_file_bytes).hexdigest()
177208
package["sha256"] = new_file_hash
178-
209+
179210
with open(dist / "pyodide-lock.json", "w") as file:
180211
json.dump(lock, file)
181212

213+
182214
if __name__ == "__main__":
183215
if len(sys.argv) != 2:
184216
print("Usage: python script.py <tag>")
@@ -187,11 +219,14 @@ def convert_wheels_to_tar_gz(dist = Path("dist")):
187219

188220
with open("required_packages.txt", "r") as file:
189221
required_packages = file.read().split("\n")
190-
status = os.system(f"pyodide build-recipes --install {' '.join(required_packages)}")
191-
if status != 0:
192-
raise Exception("Failed to build recipes")
193-
222+
result = subprocess.run(
223+
["pyodide", "build-recipes", "--install", *required_packages]
224+
)
225+
if result.returncode != 0:
226+
print("Failed to build recipes", file=sys.stderr)
227+
sys.exit(result.returncode)
228+
194229
convert_wheels_to_tar_gz()
195-
230+
196231
make_bundle(tag)
197-
upload_to_r2(tag)
232+
upload_to_r2(tag)

0 commit comments

Comments
 (0)