Skip to content

Commit 8a81ca7

Browse files
committed
Embeds Python package locks in the binary.
1 parent 00d8c87 commit 8a81ca7

File tree

12 files changed

+91
-45
lines changed

12 files changed

+91
-45
lines changed

WORKSPACE

+10-6
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,17 @@ http_archive(
9494
urls = ["https://github.com/dom96/pyodide_packages/releases/download/just-stdlib/pyodide_packages.tar.zip"],
9595
)
9696

97-
load("//:build/pyodide_bucket.bzl", "PYODIDE_ALL_WHEELS_ZIP_SHA256", "PYODIDE_GITHUB_RELEASE_URL", "PYODIDE_LOCK_SHA256")
97+
load("//:build/pyodide_bucket.bzl", "PYODIDE_ALL_WHEELS_ZIP_SHA256", "PYODIDE_GITHUB_RELEASE_URL")
98+
load("//:build/python_metadata.bzl", "PYTHON_LOCKFILES")
9899

99-
http_file(
100-
name = "pyodide-lock.json",
101-
sha256 = PYODIDE_LOCK_SHA256,
102-
url = PYODIDE_GITHUB_RELEASE_URL + "pyodide-lock.json",
103-
)
100+
[
101+
http_file(
102+
name = "pyodide-lock_" + package_date + ".json",
103+
sha256 = package_lock_sha,
104+
url = "https://github.com/cloudflare/pyodide-build-scripts/releases/download/" + package_date + "/pyodide-lock.json",
105+
)
106+
for package_date, package_lock_sha in PYTHON_LOCKFILES.items()
107+
]
104108

105109
http_archive(
106110
name = "all_pyodide_wheels",

build/pyodide_bucket.bzl

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# both edgeworker and workerd, as well as src/pyodide/BUILD.bazel
44
PYODIDE_PACKAGE_BUCKET_URL = "https://pyodide-packages.runtime-playground.workers.dev/20240513.2/"
55
PYODIDE_GITHUB_RELEASE_URL = "https://github.com/cloudflare/pyodide-build-scripts/releases/download/20240513.2/"
6-
PYODIDE_LOCK_SHA256 = "51eb3fd8dae5f551e2393ac58edfaf6a6c8d9c51b39c1584dd5d74bd7fb803fc"
76
PYODIDE_PACKAGES_TAR_ZIP_SHA256 = "b71d4c3cee3b6bd12969a788545f4159fb1eb984a7ca5de2493c4fa8479beeec"
87
PYODIDE_ALL_WHEELS_ZIP_SHA256 = "c17feb45fdcb4b41eab9c719e69c9e062a8fc88344fcb6bbd7de0de92c3ae660"
98

build/python_metadata.bzl

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# The below is a list of pyodide-lock.json files for each package bundle version that we support.
2+
# Each of these gets embedded in the workerd and EW binary.
3+
#
4+
# The key is the `packages` field in pythonSnapshotRelease and the value is the sha256 checksum of
5+
# the lock file.
6+
PYTHON_LOCKFILES = {
7+
"20240829.4": "c2d9c67ea55a672b95a3beb8d66bfbe7df736edb4bb657383b263151e7e85ef4",
8+
"20241218": "1421e9351baf24ec44d82f78b9ac26e8e0e6595bfe3f626dedb33147bfcd1998",
9+
}

src/pyodide/BUILD.bazel

+33-15
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ load("@capnp-cpp//src/capnp:cc_capnp_library.bzl", "cc_capnp_library")
66
load("//:build/capnp_embed.bzl", "capnp_embed")
77
load("//:build/js_file.bzl", "js_file")
88
load("//:build/pyodide_bucket.bzl", "PYODIDE_PACKAGE_BUCKET_URL")
9+
load("//:build/python_metadata.bzl", "PYTHON_LOCKFILES")
910
load("//:build/wd_ts_bundle.bzl", "wd_ts_bundle")
1011

1112
copy_file(
@@ -35,7 +36,19 @@ capnp_embed(
3536
copy_file(
3637
name = "pyodide_extra_capnp_file",
3738
src = "pyodide_extra.capnp",
39+
out = "generated/pyodide_extra_tmpl.capnp",
40+
)
41+
42+
expand_template(
43+
name = "pyodide_extra_expand_template@rule",
3844
out = "generated/pyodide_extra.capnp",
45+
substitutions = {
46+
"%PACKAGE_LOCKS": ",".join([
47+
"(packageDate = \"" + package_date + "\", lock = embed \"pyodide-lock_" + package_date + ".json\")"
48+
for package_date in PYTHON_LOCKFILES.keys()
49+
]),
50+
},
51+
template = "generated/pyodide_extra_tmpl.capnp",
3952
)
4053

4154
capnp_embed(
@@ -44,21 +57,17 @@ capnp_embed(
4457
deps = ["pyodide_extra_capnp_file"],
4558
)
4659

47-
capnp_embed(
48-
name = "pyodide_lock_file_embed",
49-
src = "generated/pyodide-lock.json",
50-
deps = ["pyodide-lock.js@rule"],
51-
)
52-
5360
cc_capnp_library(
5461
name = "pyodide_extra_capnp",
5562
srcs = ["generated/pyodide_extra.capnp"],
5663
visibility = ["//visibility:public"],
5764
deps = [
5865
":pyodide_extra_file_embed",
59-
":pyodide_lock_file_embed",
6066
":pyodide_packages_archive_embed",
6167
":python_entrypoint_file_embed",
68+
] + [
69+
":pyodide_lock_" + package_date + "_file_embed"
70+
for package_date in PYTHON_LOCKFILES.keys()
6271
],
6372
)
6473

@@ -74,11 +83,23 @@ copy_file(
7483
out = "generated/python_stdlib.zip",
7584
)
7685

77-
copy_file(
78-
name = "pyodide-lock.js@rule",
79-
src = "@pyodide-lock.json//file",
80-
out = "generated/pyodide-lock.json",
81-
)
86+
[
87+
copy_file(
88+
name = "pyodide-lock_" + package_date + ".json@copy_file_rule",
89+
src = "@pyodide-lock_" + package_date + ".json//file",
90+
out = "generated/pyodide-lock_" + package_date + ".json",
91+
)
92+
for package_date, package_lock_sha in PYTHON_LOCKFILES.items()
93+
]
94+
95+
[
96+
capnp_embed(
97+
name = "pyodide_lock_" + package_date + "_file_embed",
98+
src = "generated/pyodide-lock_" + package_date + ".json",
99+
deps = ["pyodide-lock_" + package_date + ".json@copy_file_rule"],
100+
)
101+
for package_date, package_lock_sha in PYTHON_LOCKFILES.items()
102+
]
82103

83104
# pyodide.asm.js patches
84105
# TODO: all of these should be fixed by linking our own Pyodide or by upstreaming.
@@ -235,15 +256,13 @@ wd_ts_bundle(
235256
import_name = "pyodide",
236257
internal_data_modules = INTERNAL_DATA_MODULES,
237258
internal_json_modules = [
238-
"generated/pyodide-lock.json",
239259
"generated/pyodide-bucket.json",
240260
],
241261
internal_modules = INTERNAL_MODULES,
242262
js_deps = [
243263
"generated/emscriptenSetup",
244264
"pyodide.asm.wasm@rule",
245265
"python_stdlib.zip@rule",
246-
"pyodide-lock.js@rule",
247266
"pyodide-bucket.json@rule",
248267
],
249268
lint = False,
@@ -279,7 +298,6 @@ genrule(
279298
":pyodide-internal_generated_emscriptenSetup.js",
280299
":pyodide-internal_generated_pyodide.asm.wasm",
281300
":pyodide-internal_generated_python_stdlib.zip",
282-
":pyodide-internal_generated_pyodide-lock.json",
283301
":pyodide-internal_generated_pyodide-bucket.json",
284302
],
285303
outs = ["pyodide.capnp.bin"],

src/pyodide/internal/metadata.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { default as MetadataReader } from 'pyodide-internal:runtime-generated/metadata';
22
import { default as PYODIDE_BUCKET } from 'pyodide-internal:generated/pyodide-bucket.json';
3-
// The pyodide-lock.json is read from the Python bundle (pyodide-capnp-bin).
4-
import { default as PYODIDE_LOCK } from 'pyodide-internal:generated/pyodide-lock.json';
53
import { default as ArtifactBundler } from 'pyodide-internal:artifacts';
64

75
export const IS_WORKERD = MetadataReader.isWorkerd();
@@ -14,7 +12,9 @@ export const LOAD_WHEELS_FROM_R2: boolean = IS_WORKERD;
1412
export const LOAD_WHEELS_FROM_ARTIFACT_BUNDLER =
1513
MetadataReader.shouldUsePackagesInArtifactBundler();
1614
export const PACKAGES_VERSION = MetadataReader.getPackagesVersion();
17-
export const LOCKFILE: PackageLock = PYODIDE_LOCK;
15+
export const LOCKFILE: PackageLock = JSON.parse(
16+
MetadataReader.getPackagesLock()
17+
);
1818
export const REQUIREMENTS = MetadataReader.getRequirements();
1919
export const MAIN_MODULE_NAME = MetadataReader.getMainModule();
2020
export const MEMORY_SNAPSHOT_READER = MetadataReader.hasMemorySnapshot()

src/pyodide/pyodide_extra.capnp

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22

33
const pythonEntrypoint :Text = embed "python-entrypoint.js";
44
const pyodidePackagesTar :Data = embed "pyodide_packages.tar";
5-
const pyodideLock :Text = embed "pyodide-lock.json";
5+
struct PackageLock {
6+
packageDate @0 :Text;
7+
lock @1 :Text;
8+
}
9+
const packageLocks :List(PackageLock) = [
10+
%PACKAGE_LOCKS
11+
];

src/pyodide/types/runtime-generated/metadata.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ declare namespace MetadataReader {
1717
const disposeMemorySnapshot: () => void;
1818
const shouldUsePackagesInArtifactBundler: () => boolean;
1919
const getPackagesVersion: () => string;
20+
const getPackagesLock: () => string;
2021
const read: (index: number, position: number, buffer: Uint8Array) => number;
2122
}
2223

src/workerd/api/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ wd_cc_library(
138138
hdrs = [
139139
"pyodide/pyodide.h",
140140
"pyodide/setup-emscripten.h",
141+
"//src/pyodide:generated/pyodide_extra.capnp.h",
141142
],
142143
implementation_deps = ["//src/workerd/util:string-buffer"],
143144
visibility = ["//visibility:public"],

src/workerd/api/pyodide/pyodide.c++

+15-16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <workerd/util/string-buffer.h>
99
#include <workerd/util/strings.h>
1010

11+
#include <pyodide/generated/pyodide_extra.capnp.h>
12+
1113
#include <kj/array.h>
1214
#include <kj/common.h>
1315
#include <kj/debug.h>
@@ -23,22 +25,6 @@ const kj::Maybe<jsg::Bundle::Reader> PyodideBundleManager::getPyodideBundle(
2325
[](const MessageBundlePair& t) { return t.bundle; });
2426
}
2527

26-
kj::Maybe<kj::String> PyodideBundleManager::getPyodideLock(
27-
PythonSnapshotRelease::Reader pythonSnapshotRelease) const {
28-
auto bundleName = getPythonBundleName(pythonSnapshotRelease);
29-
// We expect the Pyodide Bundle for the specified bundle name to already be downloaded here.
30-
auto maybeBundle = getPyodideBundle(bundleName);
31-
auto bundle = KJ_ASSERT_NONNULL(maybeBundle);
32-
for (auto module: bundle.getModules()) {
33-
if (module.which() == workerd::jsg::Module::JSON &&
34-
module.getName() == "pyodide-internal:generated/pyodide-lock.json") {
35-
return kj::str(module.getJson());
36-
}
37-
}
38-
39-
return kj::none;
40-
}
41-
4228
void PyodideBundleManager::setPyodideBundleData(
4329
kj::String version, kj::Array<unsigned char> data) const {
4430
auto wordArray = kj::arrayPtr(
@@ -400,6 +386,16 @@ kj::Array<kj::StringPtr> ArtifactBundler::getSnapshotImports() {
400386
return result.releaseAsArray();
401387
}
402388

389+
kj::Maybe<kj::String> getPyodideLock(PythonSnapshotRelease::Reader pythonSnapshotRelease) {
390+
for (auto pkgLock: *PACKAGE_LOCKS) {
391+
if (pkgLock.getPackageDate() == pythonSnapshotRelease.getPackages()) {
392+
return kj::str(pkgLock.getLock());
393+
}
394+
}
395+
396+
return kj::none;
397+
}
398+
403399
jsg::Ref<PyodideMetadataReader> makePyodideMetadataReader(Worker::Reader conf,
404400
const PythonConfig& pythonConfig,
405401
PythonSnapshotRelease::Reader pythonRelease) {
@@ -452,6 +448,8 @@ jsg::Ref<PyodideMetadataReader> makePyodideMetadataReader(Worker::Reader conf,
452448
bool createSnapshot = pythonConfig.createSnapshot;
453449
bool createBaselineSnapshot = pythonConfig.createBaselineSnapshot;
454450
bool snapshotToDisk = createSnapshot || createBaselineSnapshot;
451+
auto lock = KJ_ASSERT_NONNULL(getPyodideLock(pythonRelease),
452+
kj::str("No lock file defined for Python packages release ", pythonRelease.getPackages()));
455453

456454
// clang-format off
457455
return jsg::alloc<PyodideMetadataReader>(
@@ -460,6 +458,7 @@ jsg::Ref<PyodideMetadataReader> makePyodideMetadataReader(Worker::Reader conf,
460458
contents.finish(),
461459
requirements.finish(),
462460
kj::str(pythonRelease.getPackages()),
461+
kj::mv(lock),
463462
true /* isWorkerd */,
464463
false /* isTracing */,
465464
snapshotToDisk,

src/workerd/api/pyodide/pyodide.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class PyodideBundleManager {
2525
public:
2626
void setPyodideBundleData(kj::String version, kj::Array<unsigned char> data) const;
2727
const kj::Maybe<jsg::Bundle::Reader> getPyodideBundle(kj::StringPtr version) const;
28-
kj::Maybe<kj::String> getPyodideLock(PythonSnapshotRelease::Reader pythonSnapshotRelease) const;
2928

3029
private:
3130
struct MessageBundlePair {
@@ -79,6 +78,7 @@ class PyodideMetadataReader: public jsg::Object {
7978
kj::Array<kj::Array<kj::byte>> contents;
8079
kj::Array<kj::String> requirements;
8180
kj::String packagesVersion;
81+
kj::String packagesLock;
8282
bool isWorkerdFlag;
8383
bool isTracingFlag;
8484
bool snapshotToDisk;
@@ -92,6 +92,7 @@ class PyodideMetadataReader: public jsg::Object {
9292
kj::Array<kj::Array<kj::byte>> contents,
9393
kj::Array<kj::String> requirements,
9494
kj::String packagesVersion,
95+
kj::String packagesLock,
9596
bool isWorkerd,
9697
bool isTracing,
9798
bool snapshotToDisk,
@@ -103,6 +104,7 @@ class PyodideMetadataReader: public jsg::Object {
103104
contents(kj::mv(contents)),
104105
requirements(kj::mv(requirements)),
105106
packagesVersion(kj::mv(packagesVersion)),
107+
packagesLock(kj::mv(packagesLock)),
106108
isWorkerdFlag(isWorkerd),
107109
isTracingFlag(isTracing),
108110
snapshotToDisk(snapshotToDisk),
@@ -165,6 +167,10 @@ class PyodideMetadataReader: public jsg::Object {
165167
return kj::str(packagesVersion);
166168
}
167169

170+
kj::String getPackagesLock() {
171+
return kj::str(packagesLock);
172+
}
173+
168174
JSG_RESOURCE_TYPE(PyodideMetadataReader) {
169175
JSG_METHOD(isWorkerd);
170176
JSG_METHOD(isTracing);
@@ -181,6 +187,7 @@ class PyodideMetadataReader: public jsg::Object {
181187
JSG_METHOD(shouldSnapshotToDisk);
182188
JSG_METHOD(shouldUsePackagesInArtifactBundler);
183189
JSG_METHOD(getPackagesVersion);
190+
JSG_METHOD(getPackagesLock);
184191
JSG_METHOD(isCreatingBaselineSnapshot);
185192
}
186193

@@ -416,6 +423,8 @@ class SetupEmscripten: public jsg::Object {
416423
void visitForGc(jsg::GcVisitor& visitor);
417424
};
418425

426+
kj::Maybe<kj::String> getPyodideLock(PythonSnapshotRelease::Reader pythonSnapshotRelease);
427+
419428
using Worker = server::config::Worker;
420429

421430
jsg::Ref<PyodideMetadataReader> makePyodideMetadataReader(Worker::Reader conf,

src/workerd/io/compatibility-date.capnp

+1-1
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ struct CompatibilityFlags @0x8f8c1b68151b6cef {
684684
$compatEnableFlag("python_workers_20250116")
685685
$experimental
686686
$pythonSnapshotRelease(pyodide = "0.27.1", pyodideRevision = "2025-01-16",
687-
packages = "2024-12-18", backport = 0,
687+
packages = "20241218", backport = 0,
688688
baselineSnapshotHash = "TODO");
689689

690690
requestCfOverridesCacheRules @72 :Bool

src/workerd/server/workerd.c++

+1-1
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ class CliMain final: public SchemaFileImpl::ErrorReporter {
826826
auto version = getPythonBundleName(pythonRelease);
827827
KJ_ASSERT_NONNULL(fetchPyodideBundle(config, version), "Failed to get Pyodide bundle");
828828

829-
auto lock = KJ_ASSERT_NONNULL(config.pyodideBundleManager.getPyodideLock(pythonRelease));
829+
auto lock = KJ_ASSERT_NONNULL(api::pyodide::getPyodideLock(pythonRelease));
830830

831831
printf("%s\n", lock.cStr());
832832
fflush(stdout);

0 commit comments

Comments
 (0)