Skip to content

Commit 4ba9f25

Browse files
authored
Implements support for python_modules for packages in Python Workers (#9905)
1 parent 5b0fc9e commit 4ba9f25

File tree

6 files changed

+70
-26
lines changed

6 files changed

+70
-26
lines changed

.changeset/curvy-suns-feel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Support for Python packages in python_modules dir

packages/wrangler/e2e/dev.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,8 @@ describe.each([{ cmd: "wrangler dev" }])(
515515
from js import Response
516516
def on_fetch(request):
517517
return Response.new(f"py hello world {mul(2,3)}")`,
518-
"vendor/mod1.py": "print(42)",
519-
"vendor/mod2.py": "def hello(): return 42",
518+
"python_modules/mod1.py": "print(42)",
519+
"python_modules/mod2.py": "def hello(): return 42",
520520
"package.json": dedent`
521521
{
522522
"name": "worker",

packages/wrangler/src/__tests__/config/configuration.test.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ describe("readConfig()", () => {
2929
],
3030
"type": "PythonModule",
3131
},
32-
Object {
33-
"globs": Array [
34-
"vendor/**/*.so",
35-
],
36-
"type": "Data",
37-
},
3832
]
3933
`);
4034
});

packages/wrangler/src/__tests__/deploy.test.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12007,29 +12007,30 @@ export default{
1200712007

1200812008
it("should print vendor modules correctly in table", async () => {
1200912009
writeWranglerConfig({
12010-
main: "index.py",
12010+
main: "src/index.py",
1201112011
compatibility_flags: ["python_workers"],
1201212012
});
1201312013

1201412014
// Create main Python file
1201512015
const mainPython =
1201612016
"from js import Response;\ndef fetch(request):\n return Response.new('hello')";
12017-
await fs.promises.writeFile("index.py", mainPython);
12017+
await fs.promises.mkdir("src", { recursive: true });
12018+
await fs.promises.writeFile("src/index.py", mainPython);
1201812019

1201912020
// Create vendor directory and files
12020-
await fs.promises.mkdir("vendor", { recursive: true });
12021+
await fs.promises.mkdir("python_modules", { recursive: true });
1202112022
await fs.promises.writeFile(
12022-
"vendor/module1.so",
12023+
"python_modules/module1.so",
1202312024
"binary content for module 1"
1202412025
);
1202512026
await fs.promises.writeFile(
12026-
"vendor/module2.py",
12027+
"python_modules/module2.py",
1202712028
"# Python vendor module 2\nprint('hello')"
1202812029
);
1202912030

1203012031
// Create a regular Python module
1203112032
await fs.promises.writeFile(
12032-
"helper.py",
12033+
"src/helper.py",
1203312034
"# Helper module\ndef helper(): pass"
1203412035
);
1203512036

packages/wrangler/src/config/validation.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -333,16 +333,6 @@ function applyPythonConfig(
333333
if (!config.rules.some((rule) => rule.type === "PythonModule")) {
334334
config.rules.push({ type: "PythonModule", globs: ["**/*.py"] });
335335
}
336-
// When vendoring packages they may include certain files that will not be automatically uploaded,
337-
// this would require specifying rules in the wrangler configuration of each worker. Instead of
338-
// requiring that, we include the config implicitly here.
339-
if (
340-
!config.rules.some(
341-
(rule) => rule.type === "Data" && rule.globs.includes("vendor/**/*.so")
342-
)
343-
) {
344-
config.rules.push({ type: "Data", globs: ["vendor/**/*.so"] });
345-
}
346336
if (!config.compatibility_flags.includes("python_workers")) {
347337
throw new UserError(
348338
"The `python_workers` compatibility flag is required to use Python."

packages/wrangler/src/deployment-bundle/find-additional-modules.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ function filterPythonVendorModules(
5757
if (!isPythonEntrypoint) {
5858
return modules;
5959
}
60-
return modules.filter((m) => !m.name.startsWith("vendor/"));
60+
return modules.filter((m) => !m.name.startsWith("python_modules" + path.sep));
6161
}
6262

6363
function getPythonVendorModulesSize(modules: CfModule[]): number {
64-
const vendorModules = modules.filter((m) => m.name.startsWith("vendor/"));
64+
const vendorModules = modules.filter((m) =>
65+
m.name.startsWith("python_modules" + path.sep)
66+
);
6567
return vendorModules.reduce((total, m) => total + m.content.length, 0);
6668
}
6769

@@ -136,6 +138,58 @@ export async function findAdditionalModules(
136138
filePath: undefined,
137139
});
138140
}
141+
142+
// Look for a `python_modules` directory in the root of the project and add all the .py and .so files in it
143+
const pythonModulesDir = path.resolve(entry.projectRoot, "python_modules");
144+
const pythonModulesDirInModuleRoot = path.resolve(
145+
entry.moduleRoot,
146+
"python_modules"
147+
);
148+
149+
// Check for conflict between a `python_modules` directory in the module root and the project root.
150+
const pythonModulesExistsInModuleRoot = existsSync(
151+
pythonModulesDirInModuleRoot
152+
);
153+
if (
154+
pythonModulesExistsInModuleRoot &&
155+
entry.projectRoot !== entry.moduleRoot
156+
) {
157+
throw new UserError(
158+
"The 'python_modules' directory cannot exist in your module root. Delete it to continue."
159+
);
160+
}
161+
162+
const pythonModulesExists = existsSync(pythonModulesDir);
163+
if (pythonModulesExists) {
164+
const pythonModulesFiles = getFiles(
165+
entry.file,
166+
pythonModulesDir,
167+
pythonModulesDir,
168+
entry.projectRoot
169+
);
170+
const vendoredRules: Rule[] = [
171+
{ type: "Data", globs: ["**/*.so", "**/*.py"] },
172+
];
173+
const vendoredModules = (
174+
await matchFiles(
175+
pythonModulesFiles,
176+
pythonModulesDir,
177+
parseRules(vendoredRules)
178+
)
179+
).map((m) => {
180+
const prefixedPath = path.join("python_modules", m.name);
181+
return {
182+
...m,
183+
name: prefixedPath,
184+
};
185+
});
186+
187+
modules.push(...vendoredModules);
188+
} else {
189+
logger.debug(
190+
"Python entrypoint detected, but no python_modules directory found."
191+
);
192+
}
139193
}
140194

141195
// The modules we find might also have sourcemaps associated with them, so when we go to copy

0 commit comments

Comments
 (0)