Skip to content

Commit cf02917

Browse files
authored
Merge pull request #3789 from cloudflare/hoodmane/fix-0.27.2-packages
Don't preload dynamic libraries in newer Python compat dates
2 parents 8159d8c + 8442063 commit cf02917

File tree

19 files changed

+213
-154
lines changed

19 files changed

+213
-154
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ build-releases.sh @cloudflare/wrangler @cloudflare/workers-runtime-1
55
RELEASE.md @cloudflare/wrangler @cloudflare/workers-runtime-1
66
package.json @cloudflare/wrangler @cloudflare/workers-runtime-1
77
pnpm-lock.yaml @cloudflare/wrangler @cloudflare/workers-runtime-1
8-
types/ @cloudflare/wrangler
9-
src/cloudflare/ @cloudflare/wrangler
8+
/types/ @cloudflare/wrangler
9+
/src/cloudflare/ @cloudflare/wrangler
1010
src/workerd/tools/ @cloudflare/wrangler @cloudflare/workers-runtime-1 @cloudflare/workers-durable-objects
1111
src/workerd/io/supported-compatibility-date.txt @cloudflare/wrangler @cloudflare/workers-runtime-1
1212
src/node/ @cloudflare/workers-runtime-1 @cloudflare/workers-durable-objects @cloudflare/workers-frameworks

build/python/packages_20241218.bzl renamed to build/python/packages_20250324_1.bzl

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# This file is automatically generated by the Pyodide build script repo
22
# (https://github.com/cloudflare/pyodide-build-scripts) and should not be manually modified.
33

4-
PACKAGES_20241218 = {
4+
PACKAGES_20250324_1 = {
55
"PyJWT": [
66
"jwt",
77
],
8+
"aiohappyeyeballs": [
9+
"aiohappyeyeballs",
10+
],
811
"aiohttp": [
912
"aiohttp",
1013
],
@@ -125,11 +128,50 @@ PACKAGES_20241218 = {
125128
"packaging": [
126129
"packaging",
127130
],
131+
"propcache": [
132+
"propcache",
133+
],
128134
"pycparser": [
129135
"pycparser",
130136
],
131137
"pydantic": [
132138
"pydantic",
139+
"pydantic.alias_generators",
140+
"pydantic.aliases",
141+
"pydantic.annotated_handlers",
142+
"pydantic.class_validators",
143+
"pydantic.color",
144+
"pydantic.config",
145+
"pydantic.dataclasses",
146+
"pydantic.datetime_parse",
147+
"pydantic.decorator",
148+
"pydantic.deprecated",
149+
"pydantic.env_settings",
150+
"pydantic.error_wrappers",
151+
"pydantic.errors",
152+
"pydantic.experimental",
153+
"pydantic.fields",
154+
"pydantic.functional_serializers",
155+
"pydantic.functional_validators",
156+
"pydantic.generics",
157+
"pydantic.json",
158+
"pydantic.json_schema",
159+
"pydantic.main",
160+
"pydantic.networks",
161+
"pydantic.parse",
162+
"pydantic.plugin",
163+
"pydantic.root_model",
164+
"pydantic.schema",
165+
"pydantic.tools",
166+
"pydantic.type_adapter",
167+
"pydantic.types",
168+
"pydantic.typing",
169+
"pydantic.utils",
170+
"pydantic.v1",
171+
"pydantic.validate_call_decorator",
172+
"pydantic.validators",
173+
"pydantic.version",
174+
"pydantic.warnings",
133175
],
134176
"pydantic_core": [
135177
"pydantic_core",

build/python_metadata.bzl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("//:build/python/packages_20240829_4.bzl", "PACKAGES_20240829_4")
2-
load("//:build/python/packages_20241218.bzl", "PACKAGES_20241218")
2+
load("//:build/python/packages_20250324_1.bzl", "PACKAGES_20250324_1")
33

44
# The below is a list of pyodide-lock.json files for each package bundle version that we support.
55
# Each of these gets embedded in the workerd and EW binary.
@@ -8,15 +8,15 @@ load("//:build/python/packages_20241218.bzl", "PACKAGES_20241218")
88
# the lock file.
99
PYTHON_LOCKFILES = {
1010
"20240829.4": "c2d9c67ea55a672b95a3beb8d66bfbe7df736edb4bb657383b263151e7e85ef4",
11-
"20241218": "1421e9351baf24ec44d82f78b9ac26e8e0e6595bfe3f626dedb33147bfcd1998",
11+
"20250324.1": "3e5a9317dc0cfcf63e556034bf0e87b958bd6debcfdccdfffc8ce477cc439626",
1212
}
1313

1414
# This is a dictionary mapping a Python version with its packages version.
1515
#
1616
# NOTE: this needs to be kept in sync with compatibility-date.capnp.
1717
PYTHON_VERSION_TO_PACKAGES = {
1818
"0.26.0a2": "20240829.4",
19-
"0.27.1": "20241218",
19+
"0.27.1": "20250324.1",
2020
"development": "20240829.4",
2121
}
2222

@@ -32,7 +32,7 @@ PYTHON_VERSION_TO_PACKAGES = {
3232
# first.
3333
PYTHON_IMPORTS_TO_TEST = {
3434
"20240829.4": PACKAGES_20240829_4,
35-
"20241218": PACKAGES_20241218,
35+
"20250324.1": PACKAGES_20250324_1,
3636
}
3737

3838
# Each new package bundle should contain the same packages as the previous. We verify this
@@ -42,9 +42,9 @@ def verify_no_packages_were_removed():
4242
for i in range(0, len(package_dates) - 1):
4343
curr_pkgs = PYTHON_IMPORTS_TO_TEST[package_dates[i]]
4444
next_pkgs = PYTHON_IMPORTS_TO_TEST[package_dates[i + 1]]
45-
for pkg in curr_pkgs:
46-
if pkg not in next_pkgs:
47-
fail(pkg + " from packages version ", package_dates[i], " not in ", package_dates[i + 1])
45+
missing_pkgs = [pkg for pkg in curr_pkgs if pkg not in next_pkgs]
46+
if missing_pkgs:
47+
fail(str(missing_pkgs) + " from packages version ", package_dates[i], " not in ", package_dates[i + 1])
4848

4949
verify_no_packages_were_removed()
5050

@@ -62,8 +62,8 @@ BUNDLE_VERSION_INFO = make_bundle_version_info([
6262
"name": "0.26.0a2",
6363
"pyodide_version": "0.26.0a2",
6464
"pyodide_date": "2024-03-01",
65-
"backport": "21",
66-
"integrity": "sha256-DTYjdnJ41I1Gq5BsUcgh4DmHcgq1CZlwvWYF/UbV2v4=",
65+
"backport": "26",
66+
"integrity": "sha256-PGbANe9AeoPAf2EO4BCrw0iUox+C7CgJLGR/79r04yM=",
6767
"feature_flags": [],
6868
"emscripten_version": "3.1.52",
6969
"python_version": "3.12.1",
@@ -73,8 +73,8 @@ BUNDLE_VERSION_INFO = make_bundle_version_info([
7373
"name": "0.27.1",
7474
"pyodide_version": "0.27.1",
7575
"pyodide_date": "2025-01-16",
76-
"backport": "9",
77-
"integrity": "sha256-4c+GXQ3lL83v7z2DR1HCNGHARAUWTu669GHmlt+xpB4=",
76+
"backport": "14",
77+
"integrity": "sha256-yJduukBGEXPOlWEZkg6gz0j31Kw1AHyy/t9+FYJcagw=",
7878
"feature_flags": ["pythonWorkers20250116"],
7979
"emscripten_version": "3.1.58",
8080
"python_version": "3.12.7",

src/pyodide/BUILD.bazel

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ const location = undefined;
113113
114114
function addEventListener(){}
115115
116-
function reportUndefinedSymbolsNoOp() {}
116+
function reportUndefinedSymbolsPatched(Module) {
117+
if (Module.API.version === "0.26.0a2") {
118+
return;
119+
}
120+
Module.reportUndefinedSymbols(undefined);
121+
}
117122
118123
if (typeof FinalizationRegistry === "undefined") {
119124
globalThis.FinalizationRegistry = class FinalizationRegistry {
@@ -157,7 +162,7 @@ REPLACEMENTS = [
157162
],
158163
[
159164
"reportUndefinedSymbols()",
160-
"reportUndefinedSymbolsNoOp()",
165+
"reportUndefinedSymbolsPatched(Module)",
161166
],
162167
[
163168
"crypto.getRandomValues(",
@@ -177,9 +182,16 @@ REPLACEMENTS = [
177182
"eval(UTF8ToString(ptr))",
178183
"(() => {throw new Error('Internal Emscripten code tried to eval, this should not happen, please file a bug report with your requirements.txt file\\'s contents')})()",
179184
],
185+
# Dynamic linking patches:
186+
# library lookup
187+
[
188+
"!libData",
189+
"!(libData ??= patchDynlibLookup(Module, libName))",
190+
],
191+
# for ensuring memory base of dynlib is stable when restoring snapshots
180192
[
181-
"!libData&&flags.fs",
182-
"!(libData ??= patchDynlibLookup(Module, libName))&&flags.fs",
193+
"getMemory(",
194+
"Module.getMemoryPatched(Module, libName, ",
183195
],
184196
]
185197

src/pyodide/internal/loadPackage.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ import {
1818
USING_OLDEST_PACKAGES_VERSION,
1919
} from 'pyodide-internal:metadata';
2020
import {
21-
DYNLIB_PATH,
2221
VIRTUALIZED_DIR,
2322
STDLIB_PACKAGES,
24-
getSitePackagesPath,
2523
} from 'pyodide-internal:setupPackages';
2624
import { parseTarInfo } from 'pyodide-internal:tar';
2725
import { default as DiskCache } from 'pyodide-internal:disk_cache';

src/pyodide/internal/metadatafs.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ export function createMetadataFS(Module: Module): object {
5656
if (parent.tree == undefined) {
5757
throw new Error('cannot lookup directory, tree is undefined');
5858
}
59-
return parent.tree.get(name)!;
59+
const res = parent.tree.get(name);
60+
if (res === undefined) {
61+
throw new Module.FS.ErrnoError(44);
62+
}
63+
return res;
6064
},
6165
read(stream, position, buffer) {
6266
return MetadataReader.read(stream.node.index!, position, buffer);

src/pyodide/internal/pool/emscriptenSetup.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ function getPrepareFileSystem(pythonStdlib: ArrayBuffer): PreRunHook {
6262
try {
6363
const pymajor = Module._py_version_major();
6464
const pyminor = Module._py_version_minor();
65-
Module.FS.mkdirTree(`/lib/python${pymajor}.${pyminor}/site-packages`);
65+
Module.FS.sitePackages = `/lib/python${pymajor}.${pyminor}/site-packages`;
66+
Module.FS.sessionSitePackages = '/session' + Module.FS.sitePackages;
67+
Module.FS.mkdirTree(Module.FS.sitePackages);
6668
Module.FS.writeFile(
6769
`/lib/python${pymajor}${pyminor}.zip`,
6870
new Uint8Array(pythonStdlib),

src/pyodide/internal/setupPackages.ts

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
LOAD_WHEELS_FROM_R2,
77
LOCKFILE,
88
LOAD_WHEELS_FROM_ARTIFACT_BUNDLER,
9-
USING_OLDEST_PACKAGES_VERSION,
109
} from 'pyodide-internal:metadata';
1110
import { simpleRunPython } from 'pyodide-internal:util';
1211
import { default as EmbeddedPackagesTarReader } from 'pyodide-internal:packages_tar_reader';
1312
import { default as MetadataReader } from 'pyodide-internal:runtime-generated/metadata';
1413

1514
const canonicalizeNameRegex = /[-_.]+/g;
15+
const DYNLIB_PATH = '/usr/lib';
1616

1717
/**
1818
* Canonicalize a package name. Port of Python's packaging.utils.canonicalize_name.
@@ -153,8 +153,13 @@ class VirtualizedDir {
153153
}
154154

155155
mount(Module: Module, tarFS: EmscriptenFS<TarFSInfo>) {
156-
const path = getSitePackagesPath(Module);
157-
Module.FS.mount(tarFS, { info: this.rootInfo }, path);
156+
Module.FS.mkdirTree(Module.FS.sessionSitePackages);
157+
Module.FS.mount(
158+
tarFS,
159+
{ info: this.rootInfo },
160+
Module.FS.sessionSitePackages
161+
);
162+
Module.FS.mkdirTree(DYNLIB_PATH);
158163
Module.FS.mount(tarFS, { info: this.dynlibTarFs }, DYNLIB_PATH);
159164
}
160165
}
@@ -215,14 +220,6 @@ function disabledLoadPackage(): never {
215220
);
216221
}
217222

218-
export function getSitePackagesPath(Module: Module): string {
219-
const pymajor = Module._py_version_major();
220-
const pyminor = Module._py_version_minor();
221-
return `/session/lib/python${pymajor}.${pyminor}/site-packages`;
222-
}
223-
224-
export const DYNLIB_PATH = '/usr/lib';
225-
226223
/**
227224
* This mounts a TarFS representing the site-packages directory (which contains the Python packages)
228225
* and another TarFS representing the dynlib directory (where dynlibs like libcrypto.so live).
@@ -233,9 +230,6 @@ export const DYNLIB_PATH = '/usr/lib';
233230
*/
234231
export function mountSitePackages(Module: Module, pkgs: VirtualizedDir): void {
235232
const tarFS = createTarFS(Module);
236-
const site_packages = getSitePackagesPath(Module);
237-
Module.FS.mkdirTree(site_packages);
238-
Module.FS.mkdirTree(DYNLIB_PATH);
239233
if (!LOAD_WHEELS_FROM_R2 && !LOAD_WHEELS_FROM_ARTIFACT_BUNDLER) {
240234
// if we are not loading additional wheels, then we're done
241235
// with site-packages and we can mount it here. Otherwise, we must mount it in
@@ -262,55 +256,13 @@ export function mountWorkerFiles(Module: Module) {
262256
* Has to run after the runtime is initialized.
263257
*/
264258
export function adjustSysPath(Module: Module): void {
265-
const site_packages = getSitePackagesPath(Module);
259+
const site_packages = Module.FS.sessionSitePackages;
266260
simpleRunPython(
267261
Module,
268262
`import sys; sys.path.append("/session/metadata"); sys.path.append("${site_packages}"); del sys`
269263
);
270264
}
271265

272-
function recursiveDependencies(
273-
lockfile: PackageLock,
274-
names: string[]
275-
): PackageDeclaration[] {
276-
const toLoad = new Map();
277-
for (const name of names) {
278-
addPackageToLoad(lockfile, name, toLoad);
279-
}
280-
return Array.from(toLoad.values());
281-
}
282-
283-
/**
284-
* Recursively add a package and its dependencies to toLoad.
285-
* A helper function for recursiveDependencies.
286-
* @param name The package to add
287-
* @param toLoad The set of names of packages to load
288-
* @private
289-
*/
290-
function addPackageToLoad(
291-
lockfile: PackageLock,
292-
name: string,
293-
toLoad: Map<string, PackageDeclaration>
294-
): void {
295-
const normalizedName = canonicalizePackageName(name);
296-
if (toLoad.has(normalizedName)) {
297-
return;
298-
}
299-
const pkgInfo = lockfile.packages[normalizedName];
300-
if (!pkgInfo) {
301-
throw new Error(
302-
`It appears that a package ("${name}") you requested is not available yet in workerd. \n` +
303-
'If you would like this package to be included, please open an issue at https://github.com/cloudflare/workerd/discussions/new?category=python-packages.'
304-
);
305-
}
306-
307-
toLoad.set(normalizedName, pkgInfo);
308-
309-
for (let depName of pkgInfo.depends) {
310-
addPackageToLoad(lockfile, depName, toLoad);
311-
}
312-
}
313-
314266
export { REQUIREMENTS };
315267
export const TRANSITIVE_REQUIREMENTS =
316268
MetadataReader.getTransitiveRequirements();

0 commit comments

Comments
 (0)