@vercel/python 6.26.0 → 6.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +205 -62
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -3223,11 +3223,12 @@ __export(src_exports, {
3223
3223
  version: () => version
3224
3224
  });
3225
3225
  module.exports = __toCommonJS(src_exports);
3226
+ var import_child_process3 = require("child_process");
3226
3227
  var import_fs11 = __toESM(require("fs"));
3227
3228
  var import_path13 = require("path");
3228
3229
 
3229
3230
  // src/package-versions.ts
3230
- var VERCEL_RUNTIME_VERSION = "0.10.1";
3231
+ var VERCEL_RUNTIME_VERSION = "0.11.0";
3231
3232
  var VERCEL_WORKERS_VERSION = "0.0.13";
3232
3233
 
3233
3234
  // src/index.ts
@@ -3257,7 +3258,7 @@ var import_fs = __toESM(require("fs"));
3257
3258
  var import_os = __toESM(require("os"));
3258
3259
  var import_which = __toESM(require_lib());
3259
3260
  var import_build_utils = require("@vercel/build-utils");
3260
- var UV_VERSION = "0.9.22";
3261
+ var UV_VERSION = "0.10.11";
3261
3262
  var UV_PYTHON_PATH_PREFIX = "/uv/python/";
3262
3263
  var UV_PYTHON_DOWNLOADS_MODE = "automatic";
3263
3264
  var isWin = process.platform === "win32";
@@ -3641,7 +3642,27 @@ function detectPlatform() {
3641
3642
  osMinor = 17;
3642
3643
  }
3643
3644
  const libc = libcFamily === detectLibc.MUSL ? "musl" : "gnu";
3644
- return { osName, archName, osMajor, osMinor, os: "linux", libc };
3645
+ const SYS_PLATFORM_MAP = {
3646
+ linux: "linux",
3647
+ win32: "win32",
3648
+ darwin: "darwin"
3649
+ };
3650
+ const sysPlatform = SYS_PLATFORM_MAP[process.platform] || "linux";
3651
+ const OS_MAP = {
3652
+ linux: "linux",
3653
+ win32: "windows",
3654
+ darwin: "macos"
3655
+ };
3656
+ const detectedOs = OS_MAP[process.platform] || "linux";
3657
+ return {
3658
+ osName,
3659
+ archName,
3660
+ osMajor,
3661
+ osMinor,
3662
+ os: detectedOs,
3663
+ sysPlatform,
3664
+ libc
3665
+ };
3645
3666
  }
3646
3667
 
3647
3668
  // src/version.ts
@@ -4499,10 +4520,31 @@ To fix this, either:
4499
4520
  * Identify public packages that have no compatible wheel for the Lambda platform.
4500
4521
  * These packages must be force-bundled because `uv sync --no-build` at cold start
4501
4522
  * will refuse to build from source.
4523
+ *
4524
+ * Packages that are not reachable on the target platform (e.g. pywin32 which is
4525
+ * only a dependency when `sys_platform == 'win32'`) are excluded -- they will
4526
+ * never be installed by `uv sync` on Lambda, so their wheel compatibility is
4527
+ * irrelevant.
4502
4528
  */
4503
4529
  async findPackagesWithoutCompatibleWheels(lockFile, publicPackageNames) {
4504
4530
  const platform = detectPlatform();
4505
- const publicSet = new Set(publicPackageNames.map(import_python_analysis3.normalizePackageName));
4531
+ const reachable = await getPackagesReachableOnPlatform(
4532
+ lockFile,
4533
+ this.projectName,
4534
+ this.pythonMajor,
4535
+ this.pythonMinor,
4536
+ platform.sysPlatform,
4537
+ platform.archName
4538
+ );
4539
+ const relevantPackages = reachable ? publicPackageNames.filter(
4540
+ (name) => reachable.has((0, import_python_analysis3.normalizePackageName)(name))
4541
+ ) : publicPackageNames;
4542
+ if (relevantPackages.length < publicPackageNames.length) {
4543
+ (0, import_build_utils5.debug)(
4544
+ `Skipping wheel check for ${publicPackageNames.length - relevantPackages.length} package(s) not reachable on the target platform`
4545
+ );
4546
+ }
4547
+ const publicSet = new Set(relevantPackages.map(import_python_analysis3.normalizePackageName));
4506
4548
  const packageWheels = /* @__PURE__ */ new Map();
4507
4549
  for (const pkg of lockFile.packages) {
4508
4550
  const normalized = (0, import_python_analysis3.normalizePackageName)(pkg.name);
@@ -4511,7 +4553,7 @@ To fix this, either:
4511
4553
  }
4512
4554
  }
4513
4555
  const incompatible = [];
4514
- for (const name of publicPackageNames) {
4556
+ for (const name of relevantPackages) {
4515
4557
  const normalized = (0, import_python_analysis3.normalizePackageName)(name);
4516
4558
  const wheels = packageWheels.get(normalized);
4517
4559
  if (!wheels || wheels.length === 0) {
@@ -4550,6 +4592,64 @@ To fix this, either:
4550
4592
  return incompatible;
4551
4593
  }
4552
4594
  };
4595
+ async function getPackagesReachableOnPlatform(lockFile, projectName, pythonMajor, pythonMinor, sysPlatform, platformMachine) {
4596
+ if (!projectName)
4597
+ return null;
4598
+ const rootNormalized = (0, import_python_analysis3.normalizePackageName)(projectName);
4599
+ const packageMap = /* @__PURE__ */ new Map();
4600
+ for (const pkg of lockFile.packages) {
4601
+ packageMap.set((0, import_python_analysis3.normalizePackageName)(pkg.name), pkg);
4602
+ }
4603
+ const rootPkg = packageMap.get(rootNormalized);
4604
+ if (!rootPkg)
4605
+ return null;
4606
+ const visited = /* @__PURE__ */ new Set();
4607
+ const queue = [];
4608
+ let queueHead = 0;
4609
+ async function enqueueDeps(pkg) {
4610
+ if (!pkg.dependencies)
4611
+ return;
4612
+ for (const dep of pkg.dependencies) {
4613
+ const normalized = (0, import_python_analysis3.normalizePackageName)(dep.name);
4614
+ if (visited.has(normalized))
4615
+ continue;
4616
+ if (dep.marker) {
4617
+ try {
4618
+ const compatible = await (0, import_python_analysis3.evaluateMarker)(
4619
+ dep.marker,
4620
+ pythonMajor,
4621
+ pythonMinor,
4622
+ sysPlatform,
4623
+ platformMachine
4624
+ );
4625
+ if (!compatible) {
4626
+ (0, import_build_utils5.debug)(
4627
+ `Skipping dependency ${dep.name}: marker "${dep.marker}" not satisfied on ${sysPlatform}`
4628
+ );
4629
+ continue;
4630
+ }
4631
+ } catch (err) {
4632
+ (0, import_build_utils5.debug)(
4633
+ `Failed to evaluate marker "${dep.marker}" for ${dep.name}, including conservatively: ${err instanceof Error ? err.message : String(err)}`
4634
+ );
4635
+ }
4636
+ }
4637
+ queue.push(normalized);
4638
+ }
4639
+ }
4640
+ await enqueueDeps(rootPkg);
4641
+ while (queueHead < queue.length) {
4642
+ const current = queue[queueHead++];
4643
+ if (visited.has(current))
4644
+ continue;
4645
+ visited.add(current);
4646
+ const pkg = packageMap.get(current);
4647
+ if (pkg) {
4648
+ await enqueueDeps(pkg);
4649
+ }
4650
+ }
4651
+ return visited;
4652
+ }
4553
4653
  async function mirrorPackagesIntoVendor({
4554
4654
  venvPath,
4555
4655
  vendorDirName,
@@ -4913,6 +5013,7 @@ var import_fs6 = __toESM(require("fs"));
4913
5013
  var import_path8 = require("path");
4914
5014
  var import_build_utils7 = require("@vercel/build-utils");
4915
5015
  var import_build_utils8 = require("@vercel/build-utils");
5016
+ var import_python_analysis5 = require("@vercel/python-analysis");
4916
5017
  var PYTHON_ENTRYPOINT_FILENAMES = [
4917
5018
  "app",
4918
5019
  "index",
@@ -4943,8 +5044,9 @@ async function fileExists(filePath) {
4943
5044
  async function checkEntrypoint(workPath, relPath) {
4944
5045
  const absPath = (0, import_path8.join)(workPath, relPath);
4945
5046
  if (!await fileExists(absPath))
4946
- return false;
4947
- return (0, import_build_utils7.isPythonEntrypoint)({ fsPath: absPath });
5047
+ return null;
5048
+ const content = await import_fs6.default.promises.readFile(absPath, "utf-8");
5049
+ return (0, import_python_analysis5.findAppOrHandler)(content);
4948
5050
  }
4949
5051
  async function getPyprojectEntrypoint(workPath) {
4950
5052
  const pyprojectData = await (0, import_build_utils8.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
@@ -4970,9 +5072,10 @@ async function getPyprojectEntrypoint(workPath) {
4970
5072
  }
4971
5073
  async function findValidEntrypoint(workPath, candidates) {
4972
5074
  for (const candidate of candidates) {
4973
- if (await checkEntrypoint(workPath, candidate)) {
4974
- (0, import_build_utils7.debug)(`Detected Python entrypoint: ${candidate}`);
4975
- return candidate;
5075
+ const varName = await checkEntrypoint(workPath, candidate);
5076
+ if (varName) {
5077
+ (0, import_build_utils7.debug)(`Detected Python entrypoint: ${candidate} (variable: ${varName})`);
5078
+ return { entrypoint: candidate, variableName: varName };
4976
5079
  }
4977
5080
  }
4978
5081
  return null;
@@ -5002,9 +5105,10 @@ async function getSubdirectories(workPath) {
5002
5105
  async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
5003
5106
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5004
5107
  try {
5005
- if (await checkEntrypoint(workPath, entry)) {
5108
+ const varName = await checkEntrypoint(workPath, entry);
5109
+ if (varName) {
5006
5110
  (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5007
- return { entrypoint: entry };
5111
+ return { entrypoint: { entrypoint: entry, variableName: varName } };
5008
5112
  }
5009
5113
  const found = await findValidEntrypoint(
5010
5114
  workPath,
@@ -5019,9 +5123,10 @@ async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
5019
5123
  async function detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) {
5020
5124
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5021
5125
  try {
5022
- if (await checkEntrypoint(workPath, entry)) {
5126
+ const varName = await checkEntrypoint(workPath, entry);
5127
+ if (varName) {
5023
5128
  (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5024
- return { entrypoint: entry };
5129
+ return { entrypoint: { entrypoint: entry, variableName: varName } };
5025
5130
  }
5026
5131
  const subdirs = await getSubdirectories(workPath);
5027
5132
  const rootDirs = ["", ...subdirs];
@@ -5045,16 +5150,11 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint)
5045
5150
  if (result)
5046
5151
  return result;
5047
5152
  const pyprojectEntry = await getPyprojectEntrypoint(workPath);
5048
- if (!pyprojectEntry)
5049
- return null;
5050
- return {
5051
- entrypoint: pyprojectEntry.entrypoint,
5052
- variableName: pyprojectEntry.variableName
5053
- };
5153
+ return pyprojectEntry ? { entrypoint: pyprojectEntry } : null;
5054
5154
  }
5055
5155
 
5056
5156
  // src/start-dev-server.ts
5057
- var import_python_analysis5 = require("@vercel/python-analysis");
5157
+ var import_python_analysis6 = require("@vercel/python-analysis");
5058
5158
  var DEV_SERVER_STARTUP_TIMEOUT = 5 * 6e4;
5059
5159
  function silenceNodeWarnings() {
5060
5160
  const original = process.emitWarning.bind(
@@ -5147,7 +5247,7 @@ async function syncDependencies({
5147
5247
  const syncDir = (0, import_path9.join)(workPath, ".vercel", "python", "sync");
5148
5248
  (0, import_fs7.mkdirSync)(syncDir, { recursive: true });
5149
5249
  const tempPyproject = (0, import_path9.join)(syncDir, "pyproject.toml");
5150
- const content = (0, import_python_analysis5.stringifyManifest)(manifest.data);
5250
+ const content = (0, import_python_analysis6.stringifyManifest)(manifest.data);
5151
5251
  (0, import_fs7.writeFileSync)(tempPyproject, content, "utf8");
5152
5252
  manifestPath = tempPyproject;
5153
5253
  (0, import_build_utils9.debug)(
@@ -5589,19 +5689,18 @@ var startDevServer = async (opts) => {
5589
5689
  installGlobalCleanupHandlers();
5590
5690
  const env = { ...process.env, ...meta.env || {} };
5591
5691
  const serviceType = env.VERCEL_SERVICE_TYPE;
5592
- let entry;
5593
- let variableName;
5692
+ let resolved;
5594
5693
  if ((serviceType === "cron" || serviceType === "worker") && rawEntrypoint?.endsWith(".py")) {
5595
- entry = rawEntrypoint;
5694
+ resolved = { entrypoint: rawEntrypoint, variableName: "app" };
5596
5695
  } else {
5597
5696
  const detected = await detectPythonEntrypoint(
5598
5697
  framework,
5599
5698
  workPath,
5600
5699
  rawEntrypoint
5601
5700
  );
5602
- entry = detected?.entrypoint;
5603
- variableName = detected?.variableName;
5604
- if (!entry) {
5701
+ if (detected?.entrypoint) {
5702
+ resolved = detected.entrypoint;
5703
+ } else {
5605
5704
  const hookResult = await runFrameworkHook(framework, {
5606
5705
  pythonEnv: env,
5607
5706
  projectDir: (0, import_path9.join)(workPath, detected?.baseDir ?? ""),
@@ -5609,10 +5708,9 @@ var startDevServer = async (opts) => {
5609
5708
  entrypoint: rawEntrypoint,
5610
5709
  detected: detected ?? void 0
5611
5710
  });
5612
- entry = hookResult?.entrypoint;
5613
- variableName = hookResult?.variableName;
5711
+ resolved = hookResult?.entrypoint;
5614
5712
  }
5615
- if (!entry) {
5713
+ if (!resolved) {
5616
5714
  const searched = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
5617
5715
  throw new import_build_utils9.NowBuildError({
5618
5716
  code: "PYTHON_ENTRYPOINT_NOT_FOUND",
@@ -5622,6 +5720,7 @@ var startDevServer = async (opts) => {
5622
5720
  });
5623
5721
  }
5624
5722
  }
5723
+ const { entrypoint: entry, variableName } = resolved;
5625
5724
  const modulePath = entry.replace(/\.py$/i, "").replace(/[\\/]/g, ".");
5626
5725
  let childProcess = null;
5627
5726
  let stdoutLogListener = null;
@@ -5813,7 +5912,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
5813
5912
 
5814
5913
  // src/quirks/index.ts
5815
5914
  var import_build_utils12 = require("@vercel/build-utils");
5816
- var import_python_analysis7 = require("@vercel/python-analysis");
5915
+ var import_python_analysis8 = require("@vercel/python-analysis");
5817
5916
 
5818
5917
  // src/quirks/matplotlib.ts
5819
5918
  var matplotlibQuirk = {
@@ -5893,7 +5992,7 @@ var import_fs9 = __toESM(require("fs"));
5893
5992
  var import_path11 = require("path");
5894
5993
  var import_execa4 = __toESM(require_execa());
5895
5994
  var import_build_utils11 = require("@vercel/build-utils");
5896
- var import_python_analysis6 = require("@vercel/python-analysis");
5995
+ var import_python_analysis7 = require("@vercel/python-analysis");
5897
5996
  function execErrorMessage(err) {
5898
5997
  if (err != null && typeof err === "object" && "stderr" in err) {
5899
5998
  const stderr = String(err.stderr);
@@ -6153,7 +6252,7 @@ echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIM
6153
6252
  (0, import_path11.join)(sitePackages, "prisma"),
6154
6253
  sitePackages
6155
6254
  );
6156
- const count = await (0, import_python_analysis6.extendDistRecord)(sitePackages, "prisma", allFiles);
6255
+ const count = await (0, import_python_analysis7.extendDistRecord)(sitePackages, "prisma", allFiles);
6157
6256
  if (count > 0) {
6158
6257
  (0, import_build_utils11.debug)(`Appended ${count} entries to prisma RECORD`);
6159
6258
  }
@@ -6178,7 +6277,7 @@ var quirks = [litellmQuirk, prismaQuirk, matplotlibQuirk];
6178
6277
  function toposortQuirks(activated) {
6179
6278
  const nameToQuirk = /* @__PURE__ */ new Map();
6180
6279
  for (const q of activated) {
6181
- nameToQuirk.set((0, import_python_analysis7.normalizePackageName)(q.dependency), q);
6280
+ nameToQuirk.set((0, import_python_analysis8.normalizePackageName)(q.dependency), q);
6182
6281
  }
6183
6282
  const adj = /* @__PURE__ */ new Map();
6184
6283
  const inDegree = /* @__PURE__ */ new Map();
@@ -6189,7 +6288,7 @@ function toposortQuirks(activated) {
6189
6288
  for (const q of activated) {
6190
6289
  if (q.runsBefore) {
6191
6290
  for (const dep of q.runsBefore) {
6192
- const target = nameToQuirk.get((0, import_python_analysis7.normalizePackageName)(dep));
6291
+ const target = nameToQuirk.get((0, import_python_analysis8.normalizePackageName)(dep));
6193
6292
  if (target) {
6194
6293
  adj.get(q).add(target);
6195
6294
  inDegree.set(target, inDegree.get(target) + 1);
@@ -6198,7 +6297,7 @@ function toposortQuirks(activated) {
6198
6297
  }
6199
6298
  if (q.runsAfter) {
6200
6299
  for (const dep of q.runsAfter) {
6201
- const source = nameToQuirk.get((0, import_python_analysis7.normalizePackageName)(dep));
6300
+ const source = nameToQuirk.get((0, import_python_analysis8.normalizePackageName)(dep));
6202
6301
  if (source) {
6203
6302
  adj.get(source).add(q);
6204
6303
  inDegree.set(q, inDegree.get(q) + 1);
@@ -6239,14 +6338,14 @@ async function runQuirks(ctx) {
6239
6338
  const installedNames = /* @__PURE__ */ new Set();
6240
6339
  const sitePackageDirs = await getVenvSitePackagesDirs(ctx.venvPath);
6241
6340
  for (const dir of sitePackageDirs) {
6242
- const distributions = await (0, import_python_analysis7.scanDistributions)(dir);
6341
+ const distributions = await (0, import_python_analysis8.scanDistributions)(dir);
6243
6342
  for (const name of distributions.keys()) {
6244
- installedNames.add((0, import_python_analysis7.normalizePackageName)(name));
6343
+ installedNames.add((0, import_python_analysis8.normalizePackageName)(name));
6245
6344
  }
6246
6345
  }
6247
6346
  const activated = quirks.filter((quirk) => {
6248
6347
  const installed = installedNames.has(
6249
- (0, import_python_analysis7.normalizePackageName)(quirk.dependency)
6348
+ (0, import_python_analysis8.normalizePackageName)(quirk.dependency)
6250
6349
  );
6251
6350
  if (!installed) {
6252
6351
  (0, import_build_utils12.debug)(`Quirk "${quirk.dependency}": not installed, skipping`);
@@ -6325,6 +6424,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6325
6424
  return {
6326
6425
  staticSourceDirs,
6327
6426
  staticRoot: staticRoot ? (0, import_path12.resolve)(workPath, staticRoot) : null,
6427
+ cdnOutputDir: null,
6328
6428
  manifestRelPath: null
6329
6429
  };
6330
6430
  }
@@ -6376,14 +6476,15 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6376
6476
  return {
6377
6477
  staticSourceDirs,
6378
6478
  staticRoot: staticRoot ? (0, import_path12.resolve)(workPath, staticRoot) : null,
6479
+ cdnOutputDir: outputStaticDir,
6379
6480
  manifestRelPath
6380
6481
  };
6381
6482
  }
6382
6483
 
6383
6484
  // src/index.ts
6384
- var import_python_analysis8 = require("@vercel/python-analysis");
6485
+ var import_python_analysis9 = require("@vercel/python-analysis");
6385
6486
  var writeFile = import_fs11.default.promises.writeFile;
6386
- var version = 3;
6487
+ var version = -1;
6387
6488
  async function runFrameworkHook(framework, ctx) {
6388
6489
  const hook = framework ? frameworkHooks[framework] : void 0;
6389
6490
  return hook?.(ctx);
@@ -6405,28 +6506,27 @@ var frameworkHooks = {
6405
6506
  if (!settingsResult)
6406
6507
  return;
6407
6508
  const { djangoSettings, settingsModule } = settingsResult;
6408
- let entrypoint;
6409
- let variableName;
6509
+ let resolvedEntrypoint;
6410
6510
  const baseDir = detected?.baseDir ?? "";
6411
6511
  const asgiApp = djangoSettings["ASGI_APPLICATION"];
6412
6512
  if (typeof asgiApp === "string") {
6413
6513
  const parts = asgiApp.split(".");
6414
- variableName = parts.at(-1);
6514
+ const variableName = parts.at(-1);
6415
6515
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6416
- entrypoint = baseDir ? `${baseDir}/${rel}` : rel;
6417
- (0, import_build_utils14.debug)(
6418
- `Django hook: ASGI entrypoint: ${entrypoint} (variable: ${variableName})`
6419
- );
6516
+ const ep = baseDir ? `${baseDir}/${rel}` : rel;
6517
+ (0, import_build_utils14.debug)(`Django hook: ASGI entrypoint: ${ep} (variable: ${variableName})`);
6518
+ resolvedEntrypoint = { entrypoint: ep, variableName };
6420
6519
  } else {
6421
6520
  const wsgiApp = djangoSettings["WSGI_APPLICATION"];
6422
6521
  if (typeof wsgiApp === "string") {
6423
6522
  const parts = wsgiApp.split(".");
6424
- variableName = parts.at(-1);
6523
+ const variableName = parts.at(-1);
6425
6524
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6426
- entrypoint = baseDir ? `${baseDir}/${rel}` : rel;
6525
+ const ep = baseDir ? `${baseDir}/${rel}` : rel;
6427
6526
  (0, import_build_utils14.debug)(
6428
- `Django hook: WSGI entrypoint: ${entrypoint} (variable: ${variableName})`
6527
+ `Django hook: WSGI entrypoint: ${ep} (variable: ${variableName})`
6429
6528
  );
6529
+ resolvedEntrypoint = { entrypoint: ep, variableName };
6430
6530
  }
6431
6531
  }
6432
6532
  let djangoStatic = null;
@@ -6441,7 +6541,7 @@ var frameworkHooks = {
6441
6541
  djangoSettings
6442
6542
  );
6443
6543
  }
6444
- return { entrypoint, variableName, djangoStatic };
6544
+ return { entrypoint: resolvedEntrypoint, djangoStatic };
6445
6545
  }
6446
6546
  };
6447
6547
  async function downloadFilesInWorkPath({
@@ -6454,7 +6554,8 @@ async function downloadFilesInWorkPath({
6454
6554
  let downloadedFiles = await (0, import_build_utils14.download)(files, workPath, meta);
6455
6555
  if (meta.isDev) {
6456
6556
  const { devCacheDir = (0, import_path13.join)(workPath, ".now", "cache") } = meta;
6457
- const destCache = (0, import_path13.join)(devCacheDir, (0, import_path13.basename)(entrypoint, ".py"));
6557
+ const cacheKey = (0, import_path13.basename)(entrypoint).replace(/\./g, "_");
6558
+ const destCache = (0, import_path13.join)(devCacheDir, cacheKey);
6458
6559
  await (0, import_build_utils14.download)(downloadedFiles, destCache);
6459
6560
  downloadedFiles = await (0, import_build_utils14.glob)("**", destCache);
6460
6561
  workPath = destCache;
@@ -6505,9 +6606,9 @@ var build = async ({
6505
6606
  ) ?? void 0;
6506
6607
  if (detected?.entrypoint) {
6507
6608
  (0, import_build_utils14.debug)(
6508
- `Resolved Python entrypoint to "${detected.entrypoint}" (configured "${entrypoint}" not found).`
6609
+ `Resolved Python entrypoint to "${detected.entrypoint.entrypoint}" (configured "${entrypoint}" not found).`
6509
6610
  );
6510
- entrypoint = detected.entrypoint;
6611
+ entrypoint = detected.entrypoint.entrypoint;
6511
6612
  } else {
6512
6613
  const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
6513
6614
  entrypointNotFound = new import_build_utils14.NowBuildError({
@@ -6518,6 +6619,27 @@ var build = async ({
6518
6619
  });
6519
6620
  }
6520
6621
  }
6622
+ if (!detected?.entrypoint && entrypoint.endsWith(".py") && fsFiles[entrypoint]) {
6623
+ const content = await import_fs11.default.promises.readFile(
6624
+ (0, import_path13.join)(workPath, entrypoint),
6625
+ "utf-8"
6626
+ );
6627
+ let varName = await (0, import_python_analysis9.findAppOrHandler)(content);
6628
+ if (!varName) {
6629
+ const isSpecialService = service?.type === "cron" || service?.type === "worker";
6630
+ if (isSpecialService) {
6631
+ varName = "app";
6632
+ } else if (!varName) {
6633
+ throw new import_build_utils14.NowBuildError({
6634
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
6635
+ message: `Could not find a top-level "app", "application", or "handler" in "${entrypoint}".`,
6636
+ link: "https://vercel.com/docs/functions/serverless-functions/runtimes/python",
6637
+ action: "Learn More"
6638
+ });
6639
+ }
6640
+ }
6641
+ detected = { entrypoint: { entrypoint, variableName: varName } };
6642
+ }
6521
6643
  if (entrypointNotFound && detected?.baseDir === void 0) {
6522
6644
  throw entrypointNotFound;
6523
6645
  }
@@ -6592,8 +6714,11 @@ var build = async ({
6592
6714
  let uv;
6593
6715
  try {
6594
6716
  const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
6595
- console.log(`Using uv at "${uvPath}"`);
6596
6717
  uv = new UvRunner(uvPath);
6718
+ const uvVersionOutput = (0, import_child_process3.execSync)(`${uvPath} --version`, {
6719
+ encoding: "utf8"
6720
+ }).trim();
6721
+ console.log(`Using ${uvVersionOutput}`);
6597
6722
  } catch (err) {
6598
6723
  console.log("Failed to install or locate uv");
6599
6724
  throw new Error(
@@ -6679,8 +6804,9 @@ var build = async ({
6679
6804
  entrypoint,
6680
6805
  detected
6681
6806
  });
6682
- if (entrypointNotFound && hookResult?.entrypoint) {
6683
- entrypoint = hookResult.entrypoint;
6807
+ const resolved = hookResult?.entrypoint ?? detected?.entrypoint;
6808
+ if (entrypointNotFound && resolved) {
6809
+ entrypoint = resolved.entrypoint;
6684
6810
  entrypointNotFound = void 0;
6685
6811
  }
6686
6812
  if (entrypointNotFound) {
@@ -6713,7 +6839,7 @@ var build = async ({
6713
6839
  if (handlerFunction) {
6714
6840
  const entrypointPath = (0, import_path13.join)(workPath, entrypoint);
6715
6841
  const source = await import_fs11.default.promises.readFile(entrypointPath, "utf-8");
6716
- const found = await (0, import_python_analysis8.containsTopLevelCallable)(source, handlerFunction);
6842
+ const found = await (0, import_python_analysis9.containsTopLevelCallable)(source, handlerFunction);
6717
6843
  if (!found) {
6718
6844
  throw new import_build_utils14.NowBuildError({
6719
6845
  code: "PYTHON_HANDLER_NOT_FOUND",
@@ -6727,7 +6853,7 @@ var build = async ({
6727
6853
  (0, import_build_utils14.debug)("Entrypoint with suffix is", entrypointWithSuffix);
6728
6854
  const handlerFuncEnvLine = handlerFunction ? `
6729
6855
  "__VC_HANDLER_FUNC_NAME": "${handlerFunction}",` : "";
6730
- const variableName = hookResult?.variableName ?? detected?.variableName ?? "";
6856
+ const variableName = resolved?.variableName ?? "";
6731
6857
  const runtimeTrampoline = `
6732
6858
  import importlib
6733
6859
  import os
@@ -6872,7 +6998,24 @@ from vercel_runtime.vc_init import vc_handler
6872
6998
  );
6873
6999
  }
6874
7000
  }
6875
- return { output };
7001
+ if (djangoStatic?.cdnOutputDir) {
7002
+ const lambdaPath = entrypoint.replace(/\.py$/, "");
7003
+ const staticFiles = await (0, import_build_utils14.glob)("**", { cwd: djangoStatic.cdnOutputDir });
7004
+ return {
7005
+ resultVersion: 2,
7006
+ result: {
7007
+ output: {
7008
+ [lambdaPath]: output,
7009
+ ...staticFiles
7010
+ },
7011
+ routes: [
7012
+ { handle: "filesystem" },
7013
+ { src: "/(.*)", dest: `/${lambdaPath}` }
7014
+ ]
7015
+ }
7016
+ };
7017
+ }
7018
+ return { resultVersion: 3, result: { output } };
6876
7019
  };
6877
7020
  var shouldServe = (opts) => {
6878
7021
  const framework = opts.config.framework;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.26.0",
3
+ "version": "6.28.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -15,7 +15,7 @@
15
15
  "directory": "packages/python"
16
16
  },
17
17
  "dependencies": {
18
- "@vercel/python-analysis": "0.10.1"
18
+ "@vercel/python-analysis": "0.11.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@renovatebot/pep440": "4.2.1",
@@ -36,14 +36,14 @@
36
36
  "smol-toml": "1.5.2",
37
37
  "vitest": "2.1.4",
38
38
  "which": "3.0.0",
39
- "@vercel/build-utils": "13.8.2",
40
- "@vercel/error-utils": "2.0.3",
41
- "@vercel/python-runtime": "0.10.1"
39
+ "@vercel/build-utils": "13.10.0",
40
+ "@vercel/python-runtime": "0.11.0",
41
+ "@vercel/error-utils": "2.0.3"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "node ../../utils/build-builder.mjs",
45
45
  "type-check": "tsc --noEmit",
46
- "test": "cross-env VERCEL_FORCE_PYTHON_STREAMING=1 jest --reporters=default --reporters=jest-junit --env node --verbose --runInBand --bail",
46
+ "test": "cross-env VERCEL_FORCE_PYTHON_STREAMING=1 NODE_OPTIONS=--experimental-vm-modules jest --reporters=default --reporters=jest-junit --env node --verbose --runInBand --bail",
47
47
  "test-unit": "vitest run --config ../../vitest.config.mts test/unit.test.ts",
48
48
  "test-e2e": "pnpm test test/integration-*",
49
49
  "vitest-run": "vitest -c ../../vitest.config.mts",