Skip to content

Commit 3b145f2

Browse files
committed
Embeds Python package locks in the binary.
1 parent c134deb commit 3b145f2

File tree

11 files changed

+84
-44
lines changed

11 files changed

+84
-44
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

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
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+
PYTHON_LOCKFILES = {
4+
"20240829.4": "c2d9c67ea55a672b95a3beb8d66bfbe7df736edb4bb657383b263151e7e85ef4",
5+
}

src/pyodide/BUILD.bazel

+31-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,17 @@ 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": "(packageDate = \"" + package_date + "\", lock = embed \"pyodide-lock_" + package_date + ".json\")"
47+
for package_date in PYTHON_LOCKFILES.keys()
48+
},
49+
template = "generated/pyodide_extra_tmpl.capnp",
3950
)
4051

4152
capnp_embed(
@@ -44,21 +55,17 @@ capnp_embed(
4455
deps = ["pyodide_extra_capnp_file"],
4556
)
4657

47-
capnp_embed(
48-
name = "pyodide_lock_file_embed",
49-
src = "generated/pyodide-lock.json",
50-
deps = ["pyodide-lock.js@rule"],
51-
)
52-
5358
cc_capnp_library(
5459
name = "pyodide_extra_capnp",
5560
srcs = ["generated/pyodide_extra.capnp"],
5661
visibility = ["//visibility:public"],
5762
deps = [
5863
":pyodide_extra_file_embed",
59-
":pyodide_lock_file_embed",
6064
":pyodide_packages_archive_embed",
6165
":python_entrypoint_file_embed",
66+
] + [
67+
":pyodide_lock_" + package_date + "_file_embed"
68+
for package_date in PYTHON_LOCKFILES.keys()
6269
],
6370
)
6471

@@ -74,11 +81,23 @@ copy_file(
7481
out = "generated/python_stdlib.zip",
7582
)
7683

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

83102
# pyodide.asm.js patches
84103
# TODO: all of these should be fixed by linking our own Pyodide or by upstreaming.
@@ -235,15 +254,13 @@ wd_ts_bundle(
235254
import_name = "pyodide",
236255
internal_data_modules = INTERNAL_DATA_MODULES,
237256
internal_json_modules = [
238-
"generated/pyodide-lock.json",
239257
"generated/pyodide-bucket.json",
240258
],
241259
internal_modules = INTERNAL_MODULES,
242260
js_deps = [
243261
"generated/emscriptenSetup",
244262
"pyodide.asm.wasm@rule",
245263
"python_stdlib.zip@rule",
246-
"pyodide-lock.js@rule",
247264
"pyodide-bucket.json@rule",
248265
],
249266
lint = False,
@@ -279,7 +296,6 @@ genrule(
279296
":pyodide-internal_generated_emscriptenSetup.js",
280297
":pyodide-internal_generated_pyodide.asm.wasm",
281298
":pyodide-internal_generated_python_stdlib.zip",
282-
":pyodide-internal_generated_pyodide-lock.json",
283299
":pyodide-internal_generated_pyodide-bucket.json",
284300
],
285301
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/server/workerd.c++

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

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

828828
printf("%s\n", lock.cStr());
829829
fflush(stdout);

0 commit comments

Comments
 (0)