@vercel/python 6.25.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 +239 -99
  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.0";
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";
@@ -3331,34 +3332,16 @@ var UvRunner = class {
3331
3332
  args.push("--no-editable");
3332
3333
  await this.runUvCmd(args, projectDir, venvPath);
3333
3334
  }
3334
- async lock(projectDir, options) {
3335
- const args = ["lock"];
3336
- if (options?.noBuild) {
3335
+ async lock(options) {
3336
+ const { projectDir, venvPath, noBuild, upgrade } = options;
3337
+ const args = ["lock", "--python", getVenvPythonBin(venvPath)];
3338
+ if (noBuild) {
3337
3339
  args.push("--no-build");
3338
3340
  }
3339
- if (options?.upgrade) {
3341
+ if (upgrade) {
3340
3342
  args.push("--upgrade");
3341
3343
  }
3342
- const pretty = `uv ${args.join(" ")}`;
3343
- (0, import_build_utils.debug)(`Running "${pretty}" in ${projectDir}...`);
3344
- try {
3345
- await (0, import_execa.default)(this.uvPath, args, {
3346
- cwd: projectDir,
3347
- env: getProtectedUvEnv(process.env)
3348
- });
3349
- } catch (err) {
3350
- const error = new Error(
3351
- `Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
3352
- );
3353
- if (err && typeof err === "object") {
3354
- if ("code" in err) {
3355
- error.code = err.code;
3356
- } else if ("signal" in err) {
3357
- error.code = err.signal;
3358
- }
3359
- }
3360
- throw error;
3361
- }
3344
+ await this.runUvCmd(args, projectDir, venvPath);
3362
3345
  }
3363
3346
  async addDependencies(options) {
3364
3347
  const { venvPath, projectDir, dependencies } = options;
@@ -3659,7 +3642,27 @@ function detectPlatform() {
3659
3642
  osMinor = 17;
3660
3643
  }
3661
3644
  const libc = libcFamily === detectLibc.MUSL ? "musl" : "gnu";
3662
- 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
+ };
3663
3666
  }
3664
3667
 
3665
3668
  // src/version.ts
@@ -4017,6 +4020,7 @@ function detectInstallSource(pythonPackage, rootDir) {
4017
4020
  async function ensureUvProject({
4018
4021
  workPath,
4019
4022
  rootDir,
4023
+ venvPath,
4020
4024
  pythonPackage,
4021
4025
  pythonVersion,
4022
4026
  uv,
@@ -4073,16 +4077,18 @@ async function ensureUvProject({
4073
4077
  if (workspaceLockFile) {
4074
4078
  lockPath = (0, import_path5.join)(rootDir, workspaceLockFile.path);
4075
4079
  } else {
4076
- await uv.lock(
4080
+ await uv.lock({
4077
4081
  projectDir,
4078
- requireBinaryWheels ? { noBuild: true, upgrade: true } : void 0
4079
- );
4082
+ venvPath,
4083
+ ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4084
+ });
4080
4085
  }
4081
4086
  if (generateLockFile && !lockPath) {
4082
- await uv.lock(
4087
+ await uv.lock({
4083
4088
  projectDir,
4084
- requireBinaryWheels ? { noBuild: true, upgrade: true } : void 0
4085
- );
4089
+ venvPath,
4090
+ ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4091
+ });
4086
4092
  }
4087
4093
  } else {
4088
4094
  projectDir = workPath;
@@ -4098,10 +4104,11 @@ async function ensureUvProject({
4098
4104
  });
4099
4105
  const content = (0, import_python_analysis2.stringifyManifest)(minimalManifest);
4100
4106
  await import_fs3.default.promises.writeFile(pyprojectPath, content);
4101
- await uv.lock(
4107
+ await uv.lock({
4102
4108
  projectDir,
4103
- requireBinaryWheels ? { noBuild: true, upgrade: true } : void 0
4104
- );
4109
+ venvPath,
4110
+ ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4111
+ });
4105
4112
  }
4106
4113
  const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : (0, import_path5.join)(projectDir, "uv.lock");
4107
4114
  return {
@@ -4418,6 +4425,19 @@ To fix this, either:
4418
4425
  runtimeToolingOverhead = 50 * 1024 * 1024;
4419
4426
  }
4420
4427
  }
4428
+ runtimeToolingOverhead += 4 * 1024;
4429
+ const projectDirRel = (0, import_path6.relative)(this.workPath, this.uvProjectDir);
4430
+ const uvLockRel = (0, import_path6.relative)(this.workPath, this.uvLockPath);
4431
+ const isOutsideWorkPath = projectDirRel.startsWith("..") || uvLockRel.startsWith("..");
4432
+ if (isOutsideWorkPath) {
4433
+ const pyprojectPath = (0, import_path6.join)(this.uvProjectDir, "pyproject.toml");
4434
+ const pyprojectStats = await import_fs4.default.promises.stat(pyprojectPath).catch(() => null);
4435
+ const uvLockStats = await import_fs4.default.promises.stat(this.uvLockPath).catch(() => null);
4436
+ if (pyprojectStats)
4437
+ runtimeToolingOverhead += pyprojectStats.size;
4438
+ if (uvLockStats)
4439
+ runtimeToolingOverhead += uvLockStats.size;
4440
+ }
4421
4441
  const remainingCapacity = LAMBDA_SIZE_THRESHOLD_BYTES - fixedOverhead - runtimeToolingOverhead;
4422
4442
  (0, import_build_utils5.debug)(
4423
4443
  `Fixed overhead: ${(fixedOverhead / (1024 * 1024)).toFixed(2)} MB, runtime tooling: ${(runtimeToolingOverhead / (1024 * 1024)).toFixed(2)} MB, remaining capacity for public packages: ${(remainingCapacity / (1024 * 1024)).toFixed(2)} MB`
@@ -4446,9 +4466,6 @@ To fix this, either:
4446
4466
  ...this.alwaysBundlePackages,
4447
4467
  ...forceBundledDueToWheels
4448
4468
  ];
4449
- const projectDirRel = (0, import_path6.relative)(this.workPath, this.uvProjectDir);
4450
- const uvLockRel = (0, import_path6.relative)(this.workPath, this.uvLockPath);
4451
- const isOutsideWorkPath = projectDirRel.startsWith("..") || uvLockRel.startsWith("..");
4452
4469
  if (isOutsideWorkPath) {
4453
4470
  const srcPyproject = (0, import_path6.join)(this.uvProjectDir, "pyproject.toml");
4454
4471
  files[`${UV_BUNDLE_DIR}/pyproject.toml`] = new import_build_utils5.FileFsRef({
@@ -4488,7 +4505,7 @@ To fix this, either:
4488
4505
  }
4489
4506
  }
4490
4507
  const finalBundleSize = await calculateBundleSize(files);
4491
- if (finalBundleSize > LAMBDA_SIZE_THRESHOLD_BYTES) {
4508
+ if (finalBundleSize > LAMBDA_SIZE_THRESHOLD_BYTES + 100 * 1024) {
4492
4509
  const finalSizeMB = (finalBundleSize / (1024 * 1024)).toFixed(2);
4493
4510
  const limitMB = (LAMBDA_SIZE_THRESHOLD_BYTES / (1024 * 1024)).toFixed(0);
4494
4511
  throw new import_build_utils5.NowBuildError({
@@ -4503,10 +4520,31 @@ To fix this, either:
4503
4520
  * Identify public packages that have no compatible wheel for the Lambda platform.
4504
4521
  * These packages must be force-bundled because `uv sync --no-build` at cold start
4505
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.
4506
4528
  */
4507
4529
  async findPackagesWithoutCompatibleWheels(lockFile, publicPackageNames) {
4508
4530
  const platform = detectPlatform();
4509
- 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));
4510
4548
  const packageWheels = /* @__PURE__ */ new Map();
4511
4549
  for (const pkg of lockFile.packages) {
4512
4550
  const normalized = (0, import_python_analysis3.normalizePackageName)(pkg.name);
@@ -4515,7 +4553,7 @@ To fix this, either:
4515
4553
  }
4516
4554
  }
4517
4555
  const incompatible = [];
4518
- for (const name of publicPackageNames) {
4556
+ for (const name of relevantPackages) {
4519
4557
  const normalized = (0, import_python_analysis3.normalizePackageName)(name);
4520
4558
  const wheels = packageWheels.get(normalized);
4521
4559
  if (!wheels || wheels.length === 0) {
@@ -4554,6 +4592,64 @@ To fix this, either:
4554
4592
  return incompatible;
4555
4593
  }
4556
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
+ }
4557
4653
  async function mirrorPackagesIntoVendor({
4558
4654
  venvPath,
4559
4655
  vendorDirName,
@@ -4917,6 +5013,7 @@ var import_fs6 = __toESM(require("fs"));
4917
5013
  var import_path8 = require("path");
4918
5014
  var import_build_utils7 = require("@vercel/build-utils");
4919
5015
  var import_build_utils8 = require("@vercel/build-utils");
5016
+ var import_python_analysis5 = require("@vercel/python-analysis");
4920
5017
  var PYTHON_ENTRYPOINT_FILENAMES = [
4921
5018
  "app",
4922
5019
  "index",
@@ -4947,8 +5044,9 @@ async function fileExists(filePath) {
4947
5044
  async function checkEntrypoint(workPath, relPath) {
4948
5045
  const absPath = (0, import_path8.join)(workPath, relPath);
4949
5046
  if (!await fileExists(absPath))
4950
- return false;
4951
- 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);
4952
5050
  }
4953
5051
  async function getPyprojectEntrypoint(workPath) {
4954
5052
  const pyprojectData = await (0, import_build_utils8.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
@@ -4974,9 +5072,10 @@ async function getPyprojectEntrypoint(workPath) {
4974
5072
  }
4975
5073
  async function findValidEntrypoint(workPath, candidates) {
4976
5074
  for (const candidate of candidates) {
4977
- if (await checkEntrypoint(workPath, candidate)) {
4978
- (0, import_build_utils7.debug)(`Detected Python entrypoint: ${candidate}`);
4979
- 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 };
4980
5079
  }
4981
5080
  }
4982
5081
  return null;
@@ -5006,9 +5105,10 @@ async function getSubdirectories(workPath) {
5006
5105
  async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
5007
5106
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5008
5107
  try {
5009
- if (await checkEntrypoint(workPath, entry)) {
5108
+ const varName = await checkEntrypoint(workPath, entry);
5109
+ if (varName) {
5010
5110
  (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5011
- return { entrypoint: entry };
5111
+ return { entrypoint: { entrypoint: entry, variableName: varName } };
5012
5112
  }
5013
5113
  const found = await findValidEntrypoint(
5014
5114
  workPath,
@@ -5023,9 +5123,10 @@ async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
5023
5123
  async function detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) {
5024
5124
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5025
5125
  try {
5026
- if (await checkEntrypoint(workPath, entry)) {
5126
+ const varName = await checkEntrypoint(workPath, entry);
5127
+ if (varName) {
5027
5128
  (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5028
- return { entrypoint: entry };
5129
+ return { entrypoint: { entrypoint: entry, variableName: varName } };
5029
5130
  }
5030
5131
  const subdirs = await getSubdirectories(workPath);
5031
5132
  const rootDirs = ["", ...subdirs];
@@ -5049,16 +5150,11 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint)
5049
5150
  if (result)
5050
5151
  return result;
5051
5152
  const pyprojectEntry = await getPyprojectEntrypoint(workPath);
5052
- if (!pyprojectEntry)
5053
- return null;
5054
- return {
5055
- entrypoint: pyprojectEntry.entrypoint,
5056
- variableName: pyprojectEntry.variableName
5057
- };
5153
+ return pyprojectEntry ? { entrypoint: pyprojectEntry } : null;
5058
5154
  }
5059
5155
 
5060
5156
  // src/start-dev-server.ts
5061
- var import_python_analysis5 = require("@vercel/python-analysis");
5157
+ var import_python_analysis6 = require("@vercel/python-analysis");
5062
5158
  var DEV_SERVER_STARTUP_TIMEOUT = 5 * 6e4;
5063
5159
  function silenceNodeWarnings() {
5064
5160
  const original = process.emitWarning.bind(
@@ -5151,7 +5247,7 @@ async function syncDependencies({
5151
5247
  const syncDir = (0, import_path9.join)(workPath, ".vercel", "python", "sync");
5152
5248
  (0, import_fs7.mkdirSync)(syncDir, { recursive: true });
5153
5249
  const tempPyproject = (0, import_path9.join)(syncDir, "pyproject.toml");
5154
- const content = (0, import_python_analysis5.stringifyManifest)(manifest.data);
5250
+ const content = (0, import_python_analysis6.stringifyManifest)(manifest.data);
5155
5251
  (0, import_fs7.writeFileSync)(tempPyproject, content, "utf8");
5156
5252
  manifestPath = tempPyproject;
5157
5253
  (0, import_build_utils9.debug)(
@@ -5593,19 +5689,18 @@ var startDevServer = async (opts) => {
5593
5689
  installGlobalCleanupHandlers();
5594
5690
  const env = { ...process.env, ...meta.env || {} };
5595
5691
  const serviceType = env.VERCEL_SERVICE_TYPE;
5596
- let entry;
5597
- let variableName;
5692
+ let resolved;
5598
5693
  if ((serviceType === "cron" || serviceType === "worker") && rawEntrypoint?.endsWith(".py")) {
5599
- entry = rawEntrypoint;
5694
+ resolved = { entrypoint: rawEntrypoint, variableName: "app" };
5600
5695
  } else {
5601
5696
  const detected = await detectPythonEntrypoint(
5602
5697
  framework,
5603
5698
  workPath,
5604
5699
  rawEntrypoint
5605
5700
  );
5606
- entry = detected?.entrypoint;
5607
- variableName = detected?.variableName;
5608
- if (!entry) {
5701
+ if (detected?.entrypoint) {
5702
+ resolved = detected.entrypoint;
5703
+ } else {
5609
5704
  const hookResult = await runFrameworkHook(framework, {
5610
5705
  pythonEnv: env,
5611
5706
  projectDir: (0, import_path9.join)(workPath, detected?.baseDir ?? ""),
@@ -5613,10 +5708,9 @@ var startDevServer = async (opts) => {
5613
5708
  entrypoint: rawEntrypoint,
5614
5709
  detected: detected ?? void 0
5615
5710
  });
5616
- entry = hookResult?.entrypoint;
5617
- variableName = hookResult?.variableName;
5711
+ resolved = hookResult?.entrypoint;
5618
5712
  }
5619
- if (!entry) {
5713
+ if (!resolved) {
5620
5714
  const searched = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
5621
5715
  throw new import_build_utils9.NowBuildError({
5622
5716
  code: "PYTHON_ENTRYPOINT_NOT_FOUND",
@@ -5626,6 +5720,7 @@ var startDevServer = async (opts) => {
5626
5720
  });
5627
5721
  }
5628
5722
  }
5723
+ const { entrypoint: entry, variableName } = resolved;
5629
5724
  const modulePath = entry.replace(/\.py$/i, "").replace(/[\\/]/g, ".");
5630
5725
  let childProcess = null;
5631
5726
  let stdoutLogListener = null;
@@ -5817,7 +5912,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
5817
5912
 
5818
5913
  // src/quirks/index.ts
5819
5914
  var import_build_utils12 = require("@vercel/build-utils");
5820
- var import_python_analysis7 = require("@vercel/python-analysis");
5915
+ var import_python_analysis8 = require("@vercel/python-analysis");
5821
5916
 
5822
5917
  // src/quirks/matplotlib.ts
5823
5918
  var matplotlibQuirk = {
@@ -5897,7 +5992,7 @@ var import_fs9 = __toESM(require("fs"));
5897
5992
  var import_path11 = require("path");
5898
5993
  var import_execa4 = __toESM(require_execa());
5899
5994
  var import_build_utils11 = require("@vercel/build-utils");
5900
- var import_python_analysis6 = require("@vercel/python-analysis");
5995
+ var import_python_analysis7 = require("@vercel/python-analysis");
5901
5996
  function execErrorMessage(err) {
5902
5997
  if (err != null && typeof err === "object" && "stderr" in err) {
5903
5998
  const stderr = String(err.stderr);
@@ -6157,7 +6252,7 @@ echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIM
6157
6252
  (0, import_path11.join)(sitePackages, "prisma"),
6158
6253
  sitePackages
6159
6254
  );
6160
- const count = await (0, import_python_analysis6.extendDistRecord)(sitePackages, "prisma", allFiles);
6255
+ const count = await (0, import_python_analysis7.extendDistRecord)(sitePackages, "prisma", allFiles);
6161
6256
  if (count > 0) {
6162
6257
  (0, import_build_utils11.debug)(`Appended ${count} entries to prisma RECORD`);
6163
6258
  }
@@ -6182,7 +6277,7 @@ var quirks = [litellmQuirk, prismaQuirk, matplotlibQuirk];
6182
6277
  function toposortQuirks(activated) {
6183
6278
  const nameToQuirk = /* @__PURE__ */ new Map();
6184
6279
  for (const q of activated) {
6185
- nameToQuirk.set((0, import_python_analysis7.normalizePackageName)(q.dependency), q);
6280
+ nameToQuirk.set((0, import_python_analysis8.normalizePackageName)(q.dependency), q);
6186
6281
  }
6187
6282
  const adj = /* @__PURE__ */ new Map();
6188
6283
  const inDegree = /* @__PURE__ */ new Map();
@@ -6193,7 +6288,7 @@ function toposortQuirks(activated) {
6193
6288
  for (const q of activated) {
6194
6289
  if (q.runsBefore) {
6195
6290
  for (const dep of q.runsBefore) {
6196
- const target = nameToQuirk.get((0, import_python_analysis7.normalizePackageName)(dep));
6291
+ const target = nameToQuirk.get((0, import_python_analysis8.normalizePackageName)(dep));
6197
6292
  if (target) {
6198
6293
  adj.get(q).add(target);
6199
6294
  inDegree.set(target, inDegree.get(target) + 1);
@@ -6202,7 +6297,7 @@ function toposortQuirks(activated) {
6202
6297
  }
6203
6298
  if (q.runsAfter) {
6204
6299
  for (const dep of q.runsAfter) {
6205
- const source = nameToQuirk.get((0, import_python_analysis7.normalizePackageName)(dep));
6300
+ const source = nameToQuirk.get((0, import_python_analysis8.normalizePackageName)(dep));
6206
6301
  if (source) {
6207
6302
  adj.get(source).add(q);
6208
6303
  inDegree.set(q, inDegree.get(q) + 1);
@@ -6243,14 +6338,14 @@ async function runQuirks(ctx) {
6243
6338
  const installedNames = /* @__PURE__ */ new Set();
6244
6339
  const sitePackageDirs = await getVenvSitePackagesDirs(ctx.venvPath);
6245
6340
  for (const dir of sitePackageDirs) {
6246
- const distributions = await (0, import_python_analysis7.scanDistributions)(dir);
6341
+ const distributions = await (0, import_python_analysis8.scanDistributions)(dir);
6247
6342
  for (const name of distributions.keys()) {
6248
- installedNames.add((0, import_python_analysis7.normalizePackageName)(name));
6343
+ installedNames.add((0, import_python_analysis8.normalizePackageName)(name));
6249
6344
  }
6250
6345
  }
6251
6346
  const activated = quirks.filter((quirk) => {
6252
6347
  const installed = installedNames.has(
6253
- (0, import_python_analysis7.normalizePackageName)(quirk.dependency)
6348
+ (0, import_python_analysis8.normalizePackageName)(quirk.dependency)
6254
6349
  );
6255
6350
  if (!installed) {
6256
6351
  (0, import_build_utils12.debug)(`Quirk "${quirk.dependency}": not installed, skipping`);
@@ -6329,6 +6424,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6329
6424
  return {
6330
6425
  staticSourceDirs,
6331
6426
  staticRoot: staticRoot ? (0, import_path12.resolve)(workPath, staticRoot) : null,
6427
+ cdnOutputDir: null,
6332
6428
  manifestRelPath: null
6333
6429
  };
6334
6430
  }
@@ -6380,14 +6476,15 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6380
6476
  return {
6381
6477
  staticSourceDirs,
6382
6478
  staticRoot: staticRoot ? (0, import_path12.resolve)(workPath, staticRoot) : null,
6479
+ cdnOutputDir: outputStaticDir,
6383
6480
  manifestRelPath
6384
6481
  };
6385
6482
  }
6386
6483
 
6387
6484
  // src/index.ts
6388
- var import_python_analysis8 = require("@vercel/python-analysis");
6485
+ var import_python_analysis9 = require("@vercel/python-analysis");
6389
6486
  var writeFile = import_fs11.default.promises.writeFile;
6390
- var version = 3;
6487
+ var version = -1;
6391
6488
  async function runFrameworkHook(framework, ctx) {
6392
6489
  const hook = framework ? frameworkHooks[framework] : void 0;
6393
6490
  return hook?.(ctx);
@@ -6409,28 +6506,27 @@ var frameworkHooks = {
6409
6506
  if (!settingsResult)
6410
6507
  return;
6411
6508
  const { djangoSettings, settingsModule } = settingsResult;
6412
- let entrypoint;
6413
- let variableName;
6509
+ let resolvedEntrypoint;
6414
6510
  const baseDir = detected?.baseDir ?? "";
6415
6511
  const asgiApp = djangoSettings["ASGI_APPLICATION"];
6416
6512
  if (typeof asgiApp === "string") {
6417
6513
  const parts = asgiApp.split(".");
6418
- variableName = parts.at(-1);
6514
+ const variableName = parts.at(-1);
6419
6515
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6420
- entrypoint = baseDir ? `${baseDir}/${rel}` : rel;
6421
- (0, import_build_utils14.debug)(
6422
- `Django hook: ASGI entrypoint: ${entrypoint} (variable: ${variableName})`
6423
- );
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 };
6424
6519
  } else {
6425
6520
  const wsgiApp = djangoSettings["WSGI_APPLICATION"];
6426
6521
  if (typeof wsgiApp === "string") {
6427
6522
  const parts = wsgiApp.split(".");
6428
- variableName = parts.at(-1);
6523
+ const variableName = parts.at(-1);
6429
6524
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6430
- entrypoint = baseDir ? `${baseDir}/${rel}` : rel;
6525
+ const ep = baseDir ? `${baseDir}/${rel}` : rel;
6431
6526
  (0, import_build_utils14.debug)(
6432
- `Django hook: WSGI entrypoint: ${entrypoint} (variable: ${variableName})`
6527
+ `Django hook: WSGI entrypoint: ${ep} (variable: ${variableName})`
6433
6528
  );
6529
+ resolvedEntrypoint = { entrypoint: ep, variableName };
6434
6530
  }
6435
6531
  }
6436
6532
  let djangoStatic = null;
@@ -6445,7 +6541,7 @@ var frameworkHooks = {
6445
6541
  djangoSettings
6446
6542
  );
6447
6543
  }
6448
- return { entrypoint, variableName, djangoStatic };
6544
+ return { entrypoint: resolvedEntrypoint, djangoStatic };
6449
6545
  }
6450
6546
  };
6451
6547
  async function downloadFilesInWorkPath({
@@ -6458,7 +6554,8 @@ async function downloadFilesInWorkPath({
6458
6554
  let downloadedFiles = await (0, import_build_utils14.download)(files, workPath, meta);
6459
6555
  if (meta.isDev) {
6460
6556
  const { devCacheDir = (0, import_path13.join)(workPath, ".now", "cache") } = meta;
6461
- 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);
6462
6559
  await (0, import_build_utils14.download)(downloadedFiles, destCache);
6463
6560
  downloadedFiles = await (0, import_build_utils14.glob)("**", destCache);
6464
6561
  workPath = destCache;
@@ -6509,9 +6606,9 @@ var build = async ({
6509
6606
  ) ?? void 0;
6510
6607
  if (detected?.entrypoint) {
6511
6608
  (0, import_build_utils14.debug)(
6512
- `Resolved Python entrypoint to "${detected.entrypoint}" (configured "${entrypoint}" not found).`
6609
+ `Resolved Python entrypoint to "${detected.entrypoint.entrypoint}" (configured "${entrypoint}" not found).`
6513
6610
  );
6514
- entrypoint = detected.entrypoint;
6611
+ entrypoint = detected.entrypoint.entrypoint;
6515
6612
  } else {
6516
6613
  const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
6517
6614
  entrypointNotFound = new import_build_utils14.NowBuildError({
@@ -6522,6 +6619,27 @@ var build = async ({
6522
6619
  });
6523
6620
  }
6524
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
+ }
6525
6643
  if (entrypointNotFound && detected?.baseDir === void 0) {
6526
6644
  throw entrypointNotFound;
6527
6645
  }
@@ -6596,8 +6714,11 @@ var build = async ({
6596
6714
  let uv;
6597
6715
  try {
6598
6716
  const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
6599
- console.log(`Using uv at "${uvPath}"`);
6600
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}`);
6601
6722
  } catch (err) {
6602
6723
  console.log("Failed to install or locate uv");
6603
6724
  throw new Error(
@@ -6636,6 +6757,7 @@ var build = async ({
6636
6757
  const { projectDir, lockPath, lockFileProvidedByUser } = await ensureUvProject({
6637
6758
  workPath,
6638
6759
  rootDir,
6760
+ venvPath,
6639
6761
  pythonPackage,
6640
6762
  pythonVersion: pythonVersionString(pythonVersion),
6641
6763
  uv,
@@ -6682,8 +6804,9 @@ var build = async ({
6682
6804
  entrypoint,
6683
6805
  detected
6684
6806
  });
6685
- if (entrypointNotFound && hookResult?.entrypoint) {
6686
- entrypoint = hookResult.entrypoint;
6807
+ const resolved = hookResult?.entrypoint ?? detected?.entrypoint;
6808
+ if (entrypointNotFound && resolved) {
6809
+ entrypoint = resolved.entrypoint;
6687
6810
  entrypointNotFound = void 0;
6688
6811
  }
6689
6812
  if (entrypointNotFound) {
@@ -6716,7 +6839,7 @@ var build = async ({
6716
6839
  if (handlerFunction) {
6717
6840
  const entrypointPath = (0, import_path13.join)(workPath, entrypoint);
6718
6841
  const source = await import_fs11.default.promises.readFile(entrypointPath, "utf-8");
6719
- const found = await (0, import_python_analysis8.containsTopLevelCallable)(source, handlerFunction);
6842
+ const found = await (0, import_python_analysis9.containsTopLevelCallable)(source, handlerFunction);
6720
6843
  if (!found) {
6721
6844
  throw new import_build_utils14.NowBuildError({
6722
6845
  code: "PYTHON_HANDLER_NOT_FOUND",
@@ -6730,7 +6853,7 @@ var build = async ({
6730
6853
  (0, import_build_utils14.debug)("Entrypoint with suffix is", entrypointWithSuffix);
6731
6854
  const handlerFuncEnvLine = handlerFunction ? `
6732
6855
  "__VC_HANDLER_FUNC_NAME": "${handlerFunction}",` : "";
6733
- const variableName = hookResult?.variableName ?? detected?.variableName ?? "";
6856
+ const variableName = resolved?.variableName ?? "";
6734
6857
  const runtimeTrampoline = `
6735
6858
  import importlib
6736
6859
  import os
@@ -6875,7 +6998,24 @@ from vercel_runtime.vc_init import vc_handler
6875
6998
  );
6876
6999
  }
6877
7000
  }
6878
- 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 } };
6879
7019
  };
6880
7020
  var shouldServe = (opts) => {
6881
7021
  const framework = opts.config.framework;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.25.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.0"
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",