@vercel/python 6.28.0 → 6.29.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.
package/dist/index.js CHANGED
@@ -3228,11 +3228,11 @@ var import_fs11 = __toESM(require("fs"));
3228
3228
  var import_path13 = require("path");
3229
3229
 
3230
3230
  // src/package-versions.ts
3231
- var VERCEL_RUNTIME_VERSION = "0.11.0";
3231
+ var VERCEL_RUNTIME_VERSION = "0.12.0";
3232
3232
  var VERCEL_WORKERS_VERSION = "0.0.13";
3233
3233
 
3234
3234
  // src/index.ts
3235
- var import_build_utils14 = require("@vercel/build-utils");
3235
+ var import_build_utils15 = require("@vercel/build-utils");
3236
3236
 
3237
3237
  // src/install.ts
3238
3238
  var import_execa3 = __toESM(require_execa());
@@ -5004,7 +5004,7 @@ var diagnostics = async ({
5004
5004
  var import_child_process2 = require("child_process");
5005
5005
  var import_fs7 = require("fs");
5006
5006
  var import_path9 = require("path");
5007
- var import_build_utils9 = require("@vercel/build-utils");
5007
+ var import_build_utils10 = require("@vercel/build-utils");
5008
5008
  var import_get_port = __toESM(require_get_port());
5009
5009
  var import_is_port_reachable = __toESM(require_is_port_reachable());
5010
5010
 
@@ -5013,6 +5013,7 @@ var import_fs6 = __toESM(require("fs"));
5013
5013
  var import_path8 = require("path");
5014
5014
  var import_build_utils7 = require("@vercel/build-utils");
5015
5015
  var import_build_utils8 = require("@vercel/build-utils");
5016
+ var import_build_utils9 = require("@vercel/build-utils");
5016
5017
  var import_python_analysis5 = require("@vercel/python-analysis");
5017
5018
  var PYTHON_ENTRYPOINT_FILENAMES = [
5018
5019
  "app",
@@ -5049,7 +5050,7 @@ async function checkEntrypoint(workPath, relPath) {
5049
5050
  return (0, import_python_analysis5.findAppOrHandler)(content);
5050
5051
  }
5051
5052
  async function getPyprojectEntrypoint(workPath) {
5052
- const pyprojectData = await (0, import_build_utils8.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
5053
+ const pyprojectData = await (0, import_build_utils9.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
5053
5054
  if (!pyprojectData)
5054
5055
  return null;
5055
5056
  const scripts = pyprojectData.project?.scripts;
@@ -5074,7 +5075,7 @@ async function findValidEntrypoint(workPath, candidates) {
5074
5075
  for (const candidate of candidates) {
5075
5076
  const varName = await checkEntrypoint(workPath, candidate);
5076
5077
  if (varName) {
5077
- (0, import_build_utils7.debug)(`Detected Python entrypoint: ${candidate} (variable: ${varName})`);
5078
+ (0, import_build_utils8.debug)(`Detected Python entrypoint: ${candidate} (variable: ${varName})`);
5078
5079
  return { entrypoint: candidate, variableName: varName };
5079
5080
  }
5080
5081
  }
@@ -5086,7 +5087,7 @@ async function checkDjangoManage(workPath) {
5086
5087
  const content = await import_fs6.default.promises.readFile(managePath, "utf-8");
5087
5088
  if (!content.includes("DJANGO_SETTINGS_MODULE"))
5088
5089
  return false;
5089
- (0, import_build_utils7.debug)(`Found Django manage.py with DJANGO_SETTINGS_MODULE at ${workPath}`);
5090
+ (0, import_build_utils8.debug)(`Found Django manage.py with DJANGO_SETTINGS_MODULE at ${workPath}`);
5090
5091
  return true;
5091
5092
  } catch {
5092
5093
  return false;
@@ -5102,55 +5103,78 @@ async function getSubdirectories(workPath) {
5102
5103
  return [];
5103
5104
  }
5104
5105
  }
5105
- async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
5106
- const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5106
+ function makeDetectError(framework) {
5107
+ const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
5108
+ return new import_build_utils7.NowBuildError({
5109
+ code: `${framework.toUpperCase()}_ENTRYPOINT_NOT_FOUND`,
5110
+ message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searchedList}.`,
5111
+ link: `https://vercel.com/docs/frameworks/backend/${framework}#exporting-the-${framework}-application`,
5112
+ action: "Learn More"
5113
+ });
5114
+ }
5115
+ async function detectGenericPythonEntrypoint(workPath) {
5107
5116
  try {
5108
- const varName = await checkEntrypoint(workPath, entry);
5109
- if (varName) {
5110
- (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5111
- return { entrypoint: { entrypoint: entry, variableName: varName } };
5112
- }
5113
5117
  const found = await findValidEntrypoint(
5114
5118
  workPath,
5115
5119
  PYTHON_CANDIDATE_ENTRYPOINTS
5116
5120
  );
5117
5121
  return found ? { entrypoint: found } : null;
5118
5122
  } catch {
5119
- (0, import_build_utils7.debug)("Failed to discover Python entrypoint");
5123
+ (0, import_build_utils8.debug)("Failed to discover Python entrypoint");
5120
5124
  return null;
5121
5125
  }
5122
5126
  }
5123
- async function detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) {
5124
- const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5127
+ async function detectDjangoPythonEntrypoint(workPath) {
5125
5128
  try {
5126
- const varName = await checkEntrypoint(workPath, entry);
5127
- if (varName) {
5128
- (0, import_build_utils7.debug)(`Using configured Python entrypoint: ${entry}`);
5129
- return { entrypoint: { entrypoint: entry, variableName: varName } };
5130
- }
5131
5129
  const subdirs = await getSubdirectories(workPath);
5132
5130
  const rootDirs = ["", ...subdirs];
5133
5131
  for (const rootDir of rootDirs) {
5134
5132
  const currPath = (0, import_path8.join)(workPath, rootDir);
5135
5133
  const isDjango = await checkDjangoManage(currPath);
5136
5134
  if (isDjango) {
5137
- return { baseDir: rootDir };
5135
+ return { baseDir: rootDir, error: makeDetectError("django") };
5138
5136
  }
5139
5137
  }
5140
5138
  const candidates = getCandidateEntrypointsInDirs(rootDirs);
5141
5139
  const found = await findValidEntrypoint(workPath, candidates);
5142
5140
  return found ? { entrypoint: found } : null;
5143
5141
  } catch {
5144
- (0, import_build_utils7.debug)("Failed to discover Django Python entrypoint");
5142
+ (0, import_build_utils8.debug)("Failed to discover Django Python entrypoint");
5145
5143
  return null;
5146
5144
  }
5147
5145
  }
5148
- async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint) {
5149
- const result = framework === "django" ? await detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) : await detectGenericPythonEntrypoint(workPath, configuredEntrypoint);
5146
+ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint, service) {
5147
+ if (configuredEntrypoint) {
5148
+ const entrypoint = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
5149
+ let varName = await checkEntrypoint(workPath, entrypoint);
5150
+ if (!varName) {
5151
+ const isSpecialService = service?.type === "cron" || service?.type === "worker";
5152
+ if (isSpecialService) {
5153
+ varName = "app";
5154
+ }
5155
+ }
5156
+ if (varName) {
5157
+ (0, import_build_utils8.debug)(`Using configured Python entrypoint: ${entrypoint}`);
5158
+ return { entrypoint: { entrypoint, variableName: varName } };
5159
+ } else {
5160
+ return {
5161
+ error: new import_build_utils7.NowBuildError({
5162
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
5163
+ message: `Could not find a top-level "app", "application", or "handler" in "${entrypoint}".`,
5164
+ link: "https://vercel.com/docs/functions/serverless-functions/runtimes/python",
5165
+ action: "Learn More"
5166
+ })
5167
+ };
5168
+ }
5169
+ }
5170
+ if (!framework) {
5171
+ return null;
5172
+ }
5173
+ const result = framework === "django" ? await detectDjangoPythonEntrypoint(workPath) : await detectGenericPythonEntrypoint(workPath);
5150
5174
  if (result)
5151
5175
  return result;
5152
5176
  const pyprojectEntry = await getPyprojectEntrypoint(workPath);
5153
- return pyprojectEntry ? { entrypoint: pyprojectEntry } : null;
5177
+ return pyprojectEntry ? { entrypoint: pyprojectEntry } : { error: makeDetectError(framework) };
5154
5178
  }
5155
5179
 
5156
5180
  // src/start-dev-server.ts
@@ -5240,7 +5264,7 @@ async function syncDependencies({
5240
5264
  let { manifestPath } = installInfo;
5241
5265
  const manifest = pythonPackage.manifest;
5242
5266
  if (!manifestType || !manifestPath) {
5243
- (0, import_build_utils9.debug)("No Python project manifest found, skipping dependency sync");
5267
+ (0, import_build_utils10.debug)("No Python project manifest found, skipping dependency sync");
5244
5268
  return;
5245
5269
  }
5246
5270
  if (manifest?.origin && manifestType === "pyproject.toml") {
@@ -5250,7 +5274,7 @@ async function syncDependencies({
5250
5274
  const content = (0, import_python_analysis6.stringifyManifest)(manifest.data);
5251
5275
  (0, import_fs7.writeFileSync)(tempPyproject, content, "utf8");
5252
5276
  manifestPath = tempPyproject;
5253
- (0, import_build_utils9.debug)(
5277
+ (0, import_build_utils10.debug)(
5254
5278
  `Wrote converted ${manifest.origin.kind} manifest to ${tempPyproject}`
5255
5279
  );
5256
5280
  }
@@ -5283,7 +5307,7 @@ async function syncDependencies({
5283
5307
  for (const [channel, chunk] of captured) {
5284
5308
  (channel === "stdout" ? writeOut : writeErr)(chunk.toString());
5285
5309
  }
5286
- throw new import_build_utils9.NowBuildError({
5310
+ throw new import_build_utils10.NowBuildError({
5287
5311
  code: "PYTHON_DEPENDENCY_SYNC_FAILED",
5288
5312
  message: `Failed to install Python dependencies from ${manifestType}: ${err instanceof Error ? err.message : String(err)}`
5289
5313
  });
@@ -5305,7 +5329,7 @@ async function runSync({
5305
5329
  switch (manifestType) {
5306
5330
  case "uv.lock": {
5307
5331
  if (!uvPath) {
5308
- throw new import_build_utils9.NowBuildError({
5332
+ throw new import_build_utils10.NowBuildError({
5309
5333
  code: "PYTHON_DEPENDENCY_SYNC_FAILED",
5310
5334
  message: "uv is required to install dependencies from uv.lock.",
5311
5335
  link: "https://docs.astral.sh/uv/getting-started/installation/",
@@ -5327,11 +5351,11 @@ async function runSync({
5327
5351
  break;
5328
5352
  }
5329
5353
  default:
5330
- (0, import_build_utils9.debug)(`Unknown manifest type: ${manifestType}`);
5354
+ (0, import_build_utils10.debug)(`Unknown manifest type: ${manifestType}`);
5331
5355
  return;
5332
5356
  }
5333
5357
  await new Promise((resolve3, reject) => {
5334
- (0, import_build_utils9.debug)(`Running "${spawnCmd} ${spawnArgs.join(" ")}" in ${projectDir}...`);
5358
+ (0, import_build_utils10.debug)(`Running "${spawnCmd} ${spawnArgs.join(" ")}" in ${projectDir}...`);
5335
5359
  const child = (0, import_child_process2.spawn)(spawnCmd, spawnArgs, {
5336
5360
  cwd: projectDir,
5337
5361
  env: getProtectedUvEnv(env),
@@ -5418,13 +5442,13 @@ async function doInstallVercelRuntime({
5418
5442
  `vercel_runtime-${VERCEL_RUNTIME_VERSION}.dist-info`
5419
5443
  );
5420
5444
  if ((0, import_fs7.existsSync)(distInfo)) {
5421
- (0, import_build_utils9.debug)(
5445
+ (0, import_build_utils10.debug)(
5422
5446
  `vercel-runtime ${VERCEL_RUNTIME_VERSION} already installed, skipping`
5423
5447
  );
5424
5448
  return;
5425
5449
  }
5426
5450
  }
5427
- (0, import_build_utils9.debug)(
5451
+ (0, import_build_utils10.debug)(
5428
5452
  `Installing vercel-runtime into ${targetDir} (type: ${isLocalDev ? "local" : "pypi"}, source: ${runtimeDep})`
5429
5453
  );
5430
5454
  const pip = uvPath ? { cmd: uvPath, prefix: ["pip", "install"] } : { cmd: pythonBin, prefix: ["-m", "pip", "install"] };
@@ -5439,14 +5463,14 @@ async function doInstallVercelRuntime({
5439
5463
  if (onStdout) {
5440
5464
  onStdout(data);
5441
5465
  } else {
5442
- (0, import_build_utils9.debug)(data.toString());
5466
+ (0, import_build_utils10.debug)(data.toString());
5443
5467
  }
5444
5468
  });
5445
5469
  child.stderr?.on("data", (data) => {
5446
5470
  if (onStderr) {
5447
5471
  onStderr(data);
5448
5472
  } else {
5449
- (0, import_build_utils9.debug)(data.toString());
5473
+ (0, import_build_utils10.debug)(data.toString());
5450
5474
  }
5451
5475
  });
5452
5476
  child.on("error", reject);
@@ -5514,13 +5538,13 @@ async function doInstallVercelWorkers({
5514
5538
  `vercel_workers-${VERCEL_WORKERS_VERSION}.dist-info`
5515
5539
  );
5516
5540
  if ((0, import_fs7.existsSync)(distInfo)) {
5517
- (0, import_build_utils9.debug)(
5541
+ (0, import_build_utils10.debug)(
5518
5542
  `vercel-workers ${VERCEL_WORKERS_VERSION} already installed, skipping`
5519
5543
  );
5520
5544
  return;
5521
5545
  }
5522
5546
  }
5523
- (0, import_build_utils9.debug)(
5547
+ (0, import_build_utils10.debug)(
5524
5548
  `Installing vercel-workers into ${targetDir} (type: ${isLocalDev ? "local" : "pypi"}, source: ${workersDep})`
5525
5549
  );
5526
5550
  const pip = uvPath ? { cmd: uvPath, prefix: ["pip", "install"] } : { cmd: pythonBin, prefix: ["-m", "pip", "install"] };
@@ -5535,14 +5559,14 @@ async function doInstallVercelWorkers({
5535
5559
  if (onStdout) {
5536
5560
  onStdout(data);
5537
5561
  } else {
5538
- (0, import_build_utils9.debug)(data.toString());
5562
+ (0, import_build_utils10.debug)(data.toString());
5539
5563
  }
5540
5564
  });
5541
5565
  child.stderr?.on("data", (data) => {
5542
5566
  if (onStderr) {
5543
5567
  onStderr(data);
5544
5568
  } else {
5545
- (0, import_build_utils9.debug)(data.toString());
5569
+ (0, import_build_utils10.debug)(data.toString());
5546
5570
  }
5547
5571
  });
5548
5572
  child.on("error", reject);
@@ -5572,12 +5596,12 @@ function installGlobalCleanupHandlers() {
5572
5596
  try {
5573
5597
  process.kill(info.pid, "SIGTERM");
5574
5598
  } catch (err) {
5575
- (0, import_build_utils9.debug)(`Error sending SIGTERM to ${info.pid}: ${err}`);
5599
+ (0, import_build_utils10.debug)(`Error sending SIGTERM to ${info.pid}: ${err}`);
5576
5600
  }
5577
5601
  try {
5578
5602
  process.kill(info.pid, "SIGKILL");
5579
5603
  } catch (err) {
5580
- (0, import_build_utils9.debug)(`Error sending SIGKILL to ${info.pid}: ${err}`);
5604
+ (0, import_build_utils10.debug)(`Error sending SIGKILL to ${info.pid}: ${err}`);
5581
5605
  }
5582
5606
  PERSISTENT_SERVERS.delete(key);
5583
5607
  }
@@ -5585,7 +5609,7 @@ function installGlobalCleanupHandlers() {
5585
5609
  try {
5586
5610
  restoreWarnings();
5587
5611
  } catch (err) {
5588
- (0, import_build_utils9.debug)(`Error restoring warnings: ${err}`);
5612
+ (0, import_build_utils10.debug)(`Error restoring warnings: ${err}`);
5589
5613
  }
5590
5614
  restoreWarnings = null;
5591
5615
  }
@@ -5622,26 +5646,26 @@ function createDevShim(workPath, entry, modulePath, serviceName, framework, vari
5622
5646
  const template = (0, import_fs7.readFileSync)(templatePath, "utf8");
5623
5647
  const shimSource = template.replace(/__VC_DEV_MODULE_NAME__/g, qualifiedModule).replace(/__VC_DEV_ENTRY_ABS__/g, entryAbs).replace(/__VC_DEV_FRAMEWORK__/g, framework).replace(/__VC_DEV_VARIABLE_NAME__/g, variableName);
5624
5648
  (0, import_fs7.writeFileSync)(shimPath, shimSource, "utf8");
5625
- (0, import_build_utils9.debug)(`Prepared Python dev shim at ${shimPath}`);
5649
+ (0, import_build_utils10.debug)(`Prepared Python dev shim at ${shimPath}`);
5626
5650
  return {
5627
5651
  module: DEV_SHIM_MODULE,
5628
5652
  extraPythonPath,
5629
5653
  shimDir: vercelPythonDir
5630
5654
  };
5631
5655
  } catch (err) {
5632
- (0, import_build_utils9.debug)(`Failed to prepare dev shim: ${err?.message || err}`);
5656
+ (0, import_build_utils10.debug)(`Failed to prepare dev shim: ${err?.message || err}`);
5633
5657
  return null;
5634
5658
  }
5635
5659
  }
5636
5660
  async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath) {
5637
5661
  const { pythonCmd, venvRoot } = useVirtualEnv(workPath, env, systemPython);
5638
5662
  if (venvRoot) {
5639
- (0, import_build_utils9.debug)(`Using existing virtualenv at ${venvRoot} for multi-service dev`);
5663
+ (0, import_build_utils10.debug)(`Using existing virtualenv at ${venvRoot} for multi-service dev`);
5640
5664
  return { command: pythonCmd, args: [] };
5641
5665
  }
5642
5666
  const venvPath = (0, import_path9.join)(workPath, ".venv");
5643
5667
  await ensureVenv({ pythonPath: systemPython, venvPath, uvPath, quiet: true });
5644
- (0, import_build_utils9.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
5668
+ (0, import_build_utils10.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
5645
5669
  const pythonBin = getVenvPythonBin(venvPath);
5646
5670
  const binDir = getVenvBinDir(venvPath);
5647
5671
  env.VIRTUAL_ENV = venvPath;
@@ -5654,11 +5678,12 @@ var startDevServer = async (opts) => {
5654
5678
  workPath,
5655
5679
  meta = {},
5656
5680
  config,
5681
+ service,
5657
5682
  onStdout,
5658
5683
  onStderr
5659
5684
  } = opts;
5660
5685
  const framework = config?.framework;
5661
- const serviceName = typeof meta.serviceName === "string" ? meta.serviceName : void 0;
5686
+ const serviceName = service?.name ?? (typeof meta.serviceName === "string" ? meta.serviceName : void 0);
5662
5687
  const serverKey = serviceName ? `${workPath}::${framework}::${serviceName}` : `${workPath}::${framework}`;
5663
5688
  const existing = PERSISTENT_SERVERS.get(serverKey);
5664
5689
  if (existing) {
@@ -5688,37 +5713,34 @@ var startDevServer = async (opts) => {
5688
5713
  restoreWarnings = silenceNodeWarnings();
5689
5714
  installGlobalCleanupHandlers();
5690
5715
  const env = { ...process.env, ...meta.env || {} };
5691
- const serviceType = env.VERCEL_SERVICE_TYPE;
5716
+ const entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
5692
5717
  let resolved;
5693
- if ((serviceType === "cron" || serviceType === "worker") && rawEntrypoint?.endsWith(".py")) {
5694
- resolved = { entrypoint: rawEntrypoint, variableName: "app" };
5718
+ const detected = await detectPythonEntrypoint(
5719
+ framework,
5720
+ workPath,
5721
+ entrypoint,
5722
+ service
5723
+ );
5724
+ if (detected?.entrypoint) {
5725
+ resolved = detected.entrypoint;
5695
5726
  } else {
5696
- const detected = await detectPythonEntrypoint(
5697
- framework,
5727
+ const hookResult = await runFrameworkHook(framework, {
5728
+ pythonEnv: env,
5729
+ projectDir: (0, import_path9.join)(workPath, detected?.baseDir ?? ""),
5698
5730
  workPath,
5699
- rawEntrypoint
5700
- );
5701
- if (detected?.entrypoint) {
5702
- resolved = detected.entrypoint;
5703
- } else {
5704
- const hookResult = await runFrameworkHook(framework, {
5705
- pythonEnv: env,
5706
- projectDir: (0, import_path9.join)(workPath, detected?.baseDir ?? ""),
5707
- workPath,
5708
- entrypoint: rawEntrypoint,
5709
- detected: detected ?? void 0
5710
- });
5711
- resolved = hookResult?.entrypoint;
5712
- }
5713
- if (!resolved) {
5714
- const searched = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
5715
- throw new import_build_utils9.NowBuildError({
5716
- code: "PYTHON_ENTRYPOINT_NOT_FOUND",
5717
- message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searched}.`,
5718
- link: `https://vercel.com/docs/frameworks/backend/${framework?.toLowerCase()}#exporting-the-${framework?.toLowerCase()}-application`,
5719
- action: "Learn More"
5720
- });
5731
+ entrypoint,
5732
+ detected: detected ?? void 0
5733
+ });
5734
+ resolved = hookResult?.entrypoint;
5735
+ }
5736
+ if (!resolved) {
5737
+ if (detected?.error) {
5738
+ throw detected.error;
5721
5739
  }
5740
+ throw new import_build_utils10.NowBuildError({
5741
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
5742
+ message: "No Python entrypoint could be detected. Please specify an entrypoint file."
5743
+ });
5722
5744
  }
5723
5745
  const { entrypoint: entry, variableName } = resolved;
5724
5746
  const modulePath = entry.replace(/\.py$/i, "").replace(/[\\/]/g, ".");
@@ -5744,7 +5766,7 @@ var startDevServer = async (opts) => {
5744
5766
  const yellow = "\x1B[33m";
5745
5767
  const white = "\x1B[1m";
5746
5768
  const reset = "\x1B[0m";
5747
- throw new import_build_utils9.NowBuildError({
5769
+ throw new import_build_utils10.NowBuildError({
5748
5770
  code: "PYTHON_EXTERNAL_VENV_DETECTED",
5749
5771
  message: `Detected activated venv at ${yellow}${venv}${reset}, ${white}vercel dev${reset} manages virtual environments automatically.
5750
5772
  Run ${white}deactivate${reset} and try again.`
@@ -5761,11 +5783,11 @@ Run ${white}deactivate${reset} and try again.`
5761
5783
  );
5762
5784
  spawnCommand = runner.command;
5763
5785
  spawnArgsPrefix = runner.args;
5764
- (0, import_build_utils9.debug)(
5786
+ (0, import_build_utils10.debug)(
5765
5787
  `Multi-service Python runner: ${spawnCommand} ${spawnArgsPrefix.join(" ")}`
5766
5788
  );
5767
5789
  } else if (venv) {
5768
- (0, import_build_utils9.debug)(`Running in virtualenv at ${venv}`);
5790
+ (0, import_build_utils10.debug)(`Running in virtualenv at ${venv}`);
5769
5791
  } else {
5770
5792
  const { pythonCmd: venvPythonCmd, venvRoot } = useVirtualEnv(
5771
5793
  workPath,
@@ -5774,9 +5796,9 @@ Run ${white}deactivate${reset} and try again.`
5774
5796
  );
5775
5797
  spawnCommand = venvPythonCmd;
5776
5798
  if (venvRoot) {
5777
- (0, import_build_utils9.debug)(`Using virtualenv at ${venvRoot}`);
5799
+ (0, import_build_utils10.debug)(`Using virtualenv at ${venvRoot}`);
5778
5800
  } else {
5779
- (0, import_build_utils9.debug)("No virtualenv found");
5801
+ (0, import_build_utils10.debug)("No virtualenv found");
5780
5802
  try {
5781
5803
  const yellow = "\x1B[33m";
5782
5804
  const reset = "\x1B[0m";
@@ -5857,7 +5879,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
5857
5879
  const moduleToRun = devShim?.module || modulePath;
5858
5880
  const pythonArgs = ["-u", "-m", moduleToRun];
5859
5881
  const argv = [...spawnArgsPrefix, ...pythonArgs];
5860
- (0, import_build_utils9.debug)(
5882
+ (0, import_build_utils10.debug)(
5861
5883
  `Starting Python dev server (${framework}): ${spawnCommand} ${argv.join(" ")} [PORT=${port}]`
5862
5884
  );
5863
5885
  if (process.stdout.columns) {
@@ -5911,7 +5933,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
5911
5933
  };
5912
5934
 
5913
5935
  // src/quirks/index.ts
5914
- var import_build_utils12 = require("@vercel/build-utils");
5936
+ var import_build_utils13 = require("@vercel/build-utils");
5915
5937
  var import_python_analysis8 = require("@vercel/python-analysis");
5916
5938
 
5917
5939
  // src/quirks/matplotlib.ts
@@ -5927,7 +5949,7 @@ var matplotlibQuirk = {
5927
5949
  // src/quirks/litellm.ts
5928
5950
  var import_fs8 = __toESM(require("fs"));
5929
5951
  var import_path10 = require("path");
5930
- var import_build_utils10 = require("@vercel/build-utils");
5952
+ var import_build_utils11 = require("@vercel/build-utils");
5931
5953
  var LAMBDA_ROOT = "/var/task";
5932
5954
  var CONFIG_CANDIDATES = [
5933
5955
  "litellm_config.yaml",
@@ -5962,24 +5984,24 @@ var litellmQuirk = {
5962
5984
  );
5963
5985
  try {
5964
5986
  await import_fs8.default.promises.access(schemaPath);
5965
- (0, import_build_utils10.debug)(`LiteLLM quirk: found schema at ${schemaPath}`);
5987
+ (0, import_build_utils11.debug)(`LiteLLM quirk: found schema at ${schemaPath}`);
5966
5988
  buildEnv.PRISMA_SCHEMA_PATH = schemaPath;
5967
5989
  break;
5968
5990
  } catch {
5969
5991
  }
5970
5992
  }
5971
5993
  if (!buildEnv.PRISMA_SCHEMA_PATH) {
5972
- (0, import_build_utils10.debug)("LiteLLM quirk: schema.prisma not found in any site-packages");
5994
+ (0, import_build_utils11.debug)("LiteLLM quirk: schema.prisma not found in any site-packages");
5973
5995
  }
5974
5996
  if (!process.env.CONFIG_FILE_PATH) {
5975
5997
  const configName = await findConfigFile(ctx.workPath);
5976
5998
  if (configName) {
5977
- (0, import_build_utils10.debug)(`LiteLLM quirk: found config at ${configName}`);
5999
+ (0, import_build_utils11.debug)(`LiteLLM quirk: found config at ${configName}`);
5978
6000
  buildEnv.CONFIG_FILE_PATH = (0, import_path10.join)(ctx.workPath, configName);
5979
6001
  env.CONFIG_FILE_PATH = (0, import_path10.join)(LAMBDA_ROOT, configName);
5980
6002
  }
5981
6003
  } else {
5982
- (0, import_build_utils10.debug)(
6004
+ (0, import_build_utils11.debug)(
5983
6005
  `LiteLLM quirk: CONFIG_FILE_PATH already set to ${process.env.CONFIG_FILE_PATH}`
5984
6006
  );
5985
6007
  }
@@ -5991,7 +6013,7 @@ var litellmQuirk = {
5991
6013
  var import_fs9 = __toESM(require("fs"));
5992
6014
  var import_path11 = require("path");
5993
6015
  var import_execa4 = __toESM(require_execa());
5994
- var import_build_utils11 = require("@vercel/build-utils");
6016
+ var import_build_utils12 = require("@vercel/build-utils");
5995
6017
  var import_python_analysis7 = require("@vercel/python-analysis");
5996
6018
  function execErrorMessage(err) {
5997
6019
  if (err != null && typeof err === "object" && "stderr" in err) {
@@ -6034,7 +6056,7 @@ async function findUserSchema(workPath) {
6034
6056
  await import_fs9.default.promises.access(resolved);
6035
6057
  return resolved;
6036
6058
  } catch {
6037
- (0, import_build_utils11.debug)(`PRISMA_SCHEMA_PATH=${envPath} not found at ${resolved}`);
6059
+ (0, import_build_utils12.debug)(`PRISMA_SCHEMA_PATH=${envPath} not found at ${resolved}`);
6038
6060
  return null;
6039
6061
  }
6040
6062
  }
@@ -6138,7 +6160,7 @@ var prismaQuirk = {
6138
6160
  dummySchemaPath,
6139
6161
  buildDummySchema(generatedDir)
6140
6162
  );
6141
- (0, import_build_utils11.debug)(`Running prisma generate (dummy) with cache dir: ${cacheDir}`);
6163
+ (0, import_build_utils12.debug)(`Running prisma generate (dummy) with cache dir: ${cacheDir}`);
6142
6164
  try {
6143
6165
  const dummyResult = await (0, import_execa4.default)(
6144
6166
  pythonPath,
@@ -6150,11 +6172,11 @@ var prismaQuirk = {
6150
6172
  }
6151
6173
  );
6152
6174
  if (dummyResult.stdout)
6153
- (0, import_build_utils11.debug)(`prisma generate (dummy) stdout: ${dummyResult.stdout}`);
6175
+ (0, import_build_utils12.debug)(`prisma generate (dummy) stdout: ${dummyResult.stdout}`);
6154
6176
  if (dummyResult.stderr)
6155
- (0, import_build_utils11.debug)(`prisma generate (dummy) stderr: ${dummyResult.stderr}`);
6177
+ (0, import_build_utils12.debug)(`prisma generate (dummy) stderr: ${dummyResult.stderr}`);
6156
6178
  } catch (err) {
6157
- throw new import_build_utils11.NowBuildError({
6179
+ throw new import_build_utils12.NowBuildError({
6158
6180
  code: "PRISMA_GENERATE_FAILED",
6159
6181
  message: `Prisma engine download failed during \`prisma generate\`. Check that your prisma version is compatible with this Python version.
6160
6182
  ` + execErrorMessage(err)
@@ -6173,22 +6195,22 @@ var prismaQuirk = {
6173
6195
  const destPath = (0, import_path11.join)(cacheDir, runtimeName);
6174
6196
  try {
6175
6197
  await import_fs9.default.promises.access(destPath);
6176
- (0, import_build_utils11.debug)(`Engine binary: ${runtimeName} already exists, skipping`);
6198
+ (0, import_build_utils12.debug)(`Engine binary: ${runtimeName} already exists, skipping`);
6177
6199
  } catch {
6178
- (0, import_build_utils11.debug)(`Engine binary: copying ${entry} -> ${runtimeName}`);
6200
+ (0, import_build_utils12.debug)(`Engine binary: copying ${entry} -> ${runtimeName}`);
6179
6201
  await import_fs9.default.promises.copyFile(srcPath, destPath);
6180
6202
  }
6181
6203
  engineCopied = true;
6182
6204
  }
6183
6205
  } catch (err) {
6184
- throw new import_build_utils11.NowBuildError({
6206
+ throw new import_build_utils12.NowBuildError({
6185
6207
  code: "PRISMA_ENGINE_NOT_FOUND",
6186
6208
  message: `could not read Prisma engine directory "${nodeModulesDir}". This may indicate an incompatible prisma version.
6187
6209
  ` + (err instanceof Error ? err.message : String(err))
6188
6210
  });
6189
6211
  }
6190
6212
  if (!engineCopied) {
6191
- throw new import_build_utils11.NowBuildError({
6213
+ throw new import_build_utils12.NowBuildError({
6192
6214
  code: "PRISMA_ENGINE_NOT_FOUND",
6193
6215
  message: `could not find engine binary matching "${srcBinaryPrefix}*" in "${nodeModulesDir}". This may indicate an incompatible prisma version or an unsupported platform (${process.arch}).`
6194
6216
  });
@@ -6215,14 +6237,14 @@ echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIM
6215
6237
  pythonEnv
6216
6238
  );
6217
6239
  if (clientAlreadyGenerated) {
6218
- (0, import_build_utils11.debug)(
6240
+ (0, import_build_utils12.debug)(
6219
6241
  "Prisma quirk: client already generated, skipping user schema generate"
6220
6242
  );
6221
6243
  shouldGenerate = false;
6222
6244
  }
6223
6245
  }
6224
6246
  if (shouldGenerate) {
6225
- (0, import_build_utils11.debug)(`Running prisma generate with user schema: ${userSchema}`);
6247
+ (0, import_build_utils12.debug)(`Running prisma generate with user schema: ${userSchema}`);
6226
6248
  try {
6227
6249
  const userResult = await (0, import_execa4.default)(
6228
6250
  pythonPath,
@@ -6234,11 +6256,11 @@ echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIM
6234
6256
  }
6235
6257
  );
6236
6258
  if (userResult.stdout)
6237
- (0, import_build_utils11.debug)(`prisma generate stdout: ${userResult.stdout}`);
6259
+ (0, import_build_utils12.debug)(`prisma generate stdout: ${userResult.stdout}`);
6238
6260
  if (userResult.stderr)
6239
- (0, import_build_utils11.debug)(`prisma generate stderr: ${userResult.stderr}`);
6261
+ (0, import_build_utils12.debug)(`prisma generate stderr: ${userResult.stderr}`);
6240
6262
  } catch (err) {
6241
- throw new import_build_utils11.NowBuildError({
6263
+ throw new import_build_utils12.NowBuildError({
6242
6264
  code: "PRISMA_GENERATE_FAILED",
6243
6265
  message: `\`prisma generate\` failed for schema "${userSchema}".
6244
6266
  ` + execErrorMessage(err)
@@ -6254,7 +6276,7 @@ echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIM
6254
6276
  );
6255
6277
  const count = await (0, import_python_analysis7.extendDistRecord)(sitePackages, "prisma", allFiles);
6256
6278
  if (count > 0) {
6257
- (0, import_build_utils11.debug)(`Appended ${count} entries to prisma RECORD`);
6279
+ (0, import_build_utils12.debug)(`Appended ${count} entries to prisma RECORD`);
6258
6280
  }
6259
6281
  } catch (err) {
6260
6282
  console.warn(
@@ -6348,13 +6370,13 @@ async function runQuirks(ctx) {
6348
6370
  (0, import_python_analysis8.normalizePackageName)(quirk.dependency)
6349
6371
  );
6350
6372
  if (!installed) {
6351
- (0, import_build_utils12.debug)(`Quirk "${quirk.dependency}": not installed, skipping`);
6373
+ (0, import_build_utils13.debug)(`Quirk "${quirk.dependency}": not installed, skipping`);
6352
6374
  }
6353
6375
  return installed;
6354
6376
  });
6355
6377
  const sorted = toposortQuirks(activated);
6356
6378
  for (const quirk of sorted) {
6357
- (0, import_build_utils12.debug)(`Quirk "${quirk.dependency}": detected, running fix-up`);
6379
+ (0, import_build_utils13.debug)(`Quirk "${quirk.dependency}": detected, running fix-up`);
6358
6380
  const result = await quirk.run(ctx);
6359
6381
  if (result.env) {
6360
6382
  Object.assign(mergedEnv, result.env);
@@ -6378,31 +6400,30 @@ async function runQuirks(ctx) {
6378
6400
  var import_fs10 = __toESM(require("fs"));
6379
6401
  var import_path12 = require("path");
6380
6402
  var import_execa5 = __toESM(require_execa());
6381
- var import_build_utils13 = require("@vercel/build-utils");
6403
+ var import_build_utils14 = require("@vercel/build-utils");
6382
6404
  var scriptPath = (0, import_path12.join)(__dirname, "..", "templates", "vc_django_settings.py");
6383
6405
  var script = import_fs10.default.readFileSync(scriptPath, "utf-8");
6384
6406
  async function getDjangoSettings(projectDir, env) {
6385
- try {
6386
- const { stdout } = await (0, import_execa5.default)("python", ["-c", script], {
6387
- env,
6388
- cwd: projectDir
6389
- });
6390
- const parsed = JSON.parse(stdout);
6391
- if (!parsed)
6392
- return null;
6393
- return {
6394
- settingsModule: parsed.settings_module,
6395
- djangoSettings: parsed.django_settings
6396
- };
6397
- } catch (err) {
6398
- (0, import_build_utils13.debug)(`Django hook: failed to discover settings from manage.py: ${err}`);
6399
- return null;
6407
+ const { stdout } = await (0, import_execa5.default)("python", ["-c", script], {
6408
+ env,
6409
+ cwd: projectDir
6410
+ });
6411
+ const parsed = JSON.parse(stdout);
6412
+ if (!parsed) {
6413
+ throw new Error("manage.py did not return any settings");
6400
6414
  }
6415
+ return {
6416
+ settingsModule: parsed.settings_module,
6417
+ djangoSettings: parsed.django_settings,
6418
+ djangoVersion: parsed.django_version ?? null
6419
+ };
6401
6420
  }
6402
- async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir, settingsModule, djangoSettings) {
6421
+ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir, settingsModule, djangoSettings, djangoVersion) {
6403
6422
  const pythonPath = getVenvPythonBin(venvPath);
6404
6423
  const storages = djangoSettings["STORAGES"];
6405
- const storageBackend = storages?.staticfiles?.BACKEND ?? djangoSettings["STATICFILES_STORAGE"] ?? "django.contrib.staticfiles.storage.StaticFilesStorage";
6424
+ const useLegacySetting = !djangoVersion || djangoVersion[0] < 5 || djangoVersion[0] === 5 && djangoVersion[1] < 1;
6425
+ const legacyBackend = useLegacySetting ? djangoSettings["STATICFILES_STORAGE"] : void 0;
6426
+ const storageBackend = storages?.staticfiles?.BACKEND ?? legacyBackend ?? "django.contrib.staticfiles.storage.StaticFilesStorage";
6406
6427
  const staticUrl = djangoSettings["STATIC_URL"] ?? "/static/";
6407
6428
  const staticRoot = djangoSettings["STATIC_ROOT"] != null ? String(djangoSettings["STATIC_ROOT"]) : null;
6408
6429
  const whitenoiseUseFinders = djangoSettings["WHITENOISE_USE_FINDERS"] === true;
@@ -6471,7 +6492,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6471
6492
  await import_fs10.default.promises.mkdir(resolvedStaticRoot, { recursive: true });
6472
6493
  await import_fs10.default.promises.copyFile(manifestSrc, manifestDest);
6473
6494
  manifestRelPath = (0, import_path12.relative)(workPath, manifestDest);
6474
- (0, import_build_utils13.debug)(`Copied staticfiles.json to ${manifestDest} for Lambda bundle`);
6495
+ (0, import_build_utils14.debug)(`Copied staticfiles.json to ${manifestDest} for Lambda bundle`);
6475
6496
  }
6476
6497
  return {
6477
6498
  staticSourceDirs,
@@ -6498,14 +6519,31 @@ var frameworkHooks = {
6498
6519
  detected
6499
6520
  }) => {
6500
6521
  if (detected?.baseDir === void 0) {
6501
- (0, import_build_utils14.debug)("Django hook: no manage.py detected, skipping");
6522
+ (0, import_build_utils15.debug)("Django hook: no manage.py detected, skipping");
6502
6523
  return;
6503
6524
  }
6504
- const settingsResult = await getDjangoSettings(projectDir, pythonEnv);
6505
- (0, import_build_utils14.debug)(`Django settings: ${JSON.stringify(settingsResult)}`);
6506
- if (!settingsResult)
6507
- return;
6508
- const { djangoSettings, settingsModule } = settingsResult;
6525
+ let settingsResult;
6526
+ try {
6527
+ settingsResult = await getDjangoSettings(projectDir, pythonEnv);
6528
+ } catch (err) {
6529
+ let detail;
6530
+ if (err?.code === "ENOENT") {
6531
+ detail = `command not found: python
6532
+ Hint: activate a venv or run with \`uv run vercel dev\``;
6533
+ } else {
6534
+ detail = err?.stderr || err?.message || String(err);
6535
+ }
6536
+ throw new import_build_utils15.NowBuildError({
6537
+ code: "DJANGO_SETTINGS_FAILED",
6538
+ message: `Failed to read Django application settings from ${projectDir}/manage.py:
6539
+ ${detail}`
6540
+ });
6541
+ }
6542
+ (0, import_build_utils15.debug)(`Django settings: ${JSON.stringify(settingsResult)}`);
6543
+ const { djangoSettings, settingsModule, djangoVersion } = settingsResult;
6544
+ if (djangoVersion) {
6545
+ console.log(`Django ${djangoVersion.join(".")} detected`);
6546
+ }
6509
6547
  let resolvedEntrypoint;
6510
6548
  const baseDir = detected?.baseDir ?? "";
6511
6549
  const asgiApp = djangoSettings["ASGI_APPLICATION"];
@@ -6514,7 +6552,7 @@ var frameworkHooks = {
6514
6552
  const variableName = parts.at(-1);
6515
6553
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6516
6554
  const ep = baseDir ? `${baseDir}/${rel}` : rel;
6517
- (0, import_build_utils14.debug)(`Django hook: ASGI entrypoint: ${ep} (variable: ${variableName})`);
6555
+ (0, import_build_utils15.debug)(`Django hook: ASGI entrypoint: ${ep} (variable: ${variableName})`);
6518
6556
  resolvedEntrypoint = { entrypoint: ep, variableName };
6519
6557
  } else {
6520
6558
  const wsgiApp = djangoSettings["WSGI_APPLICATION"];
@@ -6523,7 +6561,7 @@ var frameworkHooks = {
6523
6561
  const variableName = parts.at(-1);
6524
6562
  const rel = `${parts.slice(0, -1).join("/")}.py`;
6525
6563
  const ep = baseDir ? `${baseDir}/${rel}` : rel;
6526
- (0, import_build_utils14.debug)(
6564
+ (0, import_build_utils15.debug)(
6527
6565
  `Django hook: WSGI entrypoint: ${ep} (variable: ${variableName})`
6528
6566
  );
6529
6567
  resolvedEntrypoint = { entrypoint: ep, variableName };
@@ -6538,7 +6576,8 @@ var frameworkHooks = {
6538
6576
  pythonEnv,
6539
6577
  outputStaticDir,
6540
6578
  settingsModule,
6541
- djangoSettings
6579
+ djangoSettings,
6580
+ djangoVersion
6542
6581
  );
6543
6582
  }
6544
6583
  return { entrypoint: resolvedEntrypoint, djangoStatic };
@@ -6550,14 +6589,14 @@ async function downloadFilesInWorkPath({
6550
6589
  files,
6551
6590
  meta = {}
6552
6591
  }) {
6553
- (0, import_build_utils14.debug)("Downloading user files...");
6554
- let downloadedFiles = await (0, import_build_utils14.download)(files, workPath, meta);
6555
- if (meta.isDev) {
6592
+ (0, import_build_utils15.debug)("Downloading user files...");
6593
+ let downloadedFiles = await (0, import_build_utils15.download)(files, workPath, meta);
6594
+ if (meta.isDev && entrypoint) {
6556
6595
  const { devCacheDir = (0, import_path13.join)(workPath, ".now", "cache") } = meta;
6557
6596
  const cacheKey = (0, import_path13.basename)(entrypoint).replace(/\./g, "_");
6558
6597
  const destCache = (0, import_path13.join)(devCacheDir, cacheKey);
6559
- await (0, import_build_utils14.download)(downloadedFiles, destCache);
6560
- downloadedFiles = await (0, import_build_utils14.glob)("**", destCache);
6598
+ await (0, import_build_utils15.download)(downloadedFiles, destCache);
6599
+ downloadedFiles = await (0, import_build_utils15.glob)("**", destCache);
6561
6600
  workPath = destCache;
6562
6601
  }
6563
6602
  return workPath;
@@ -6566,19 +6605,20 @@ var build = async ({
6566
6605
  workPath,
6567
6606
  repoRootPath,
6568
6607
  files: originalFiles,
6569
- entrypoint,
6608
+ entrypoint: rawEntrypoint,
6570
6609
  meta = {},
6571
6610
  config,
6572
6611
  span: parentSpan,
6573
6612
  service
6574
6613
  }) => {
6575
- const builderSpan = parentSpan ?? new import_build_utils14.Span({ name: "vc.builder" });
6614
+ let entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
6615
+ const builderSpan = parentSpan ?? new import_build_utils15.Span({ name: "vc.builder" });
6576
6616
  const framework = config?.framework;
6577
6617
  const shouldInstallVercelWorkers = config?.hasWorkerServices === true;
6578
6618
  let spawnEnv;
6579
6619
  let projectInstallCommand;
6580
6620
  let hasCustomCommand = false;
6581
- (0, import_build_utils14.debug)(`workPath: ${workPath}`);
6621
+ (0, import_build_utils15.debug)(`workPath: ${workPath}`);
6582
6622
  workPath = await downloadFilesInWorkPath({
6583
6623
  workPath,
6584
6624
  files: originalFiles,
@@ -6594,56 +6634,17 @@ var build = async ({
6594
6634
  console.log('Failed to create "setup.cfg" file');
6595
6635
  throw err;
6596
6636
  }
6597
- let fsFiles = await (0, import_build_utils14.glob)("**", workPath);
6598
6637
  let detected;
6599
- let entrypointNotFound;
6600
- if ((0, import_build_utils14.isPythonFramework)(framework) && // XXX: we might want to detect anyway for django!
6601
- (!fsFiles[entrypoint] || !entrypoint.endsWith(".py"))) {
6602
- detected = await detectPythonEntrypoint(
6603
- config.framework,
6604
- workPath,
6605
- entrypoint
6606
- ) ?? void 0;
6607
- if (detected?.entrypoint) {
6608
- (0, import_build_utils14.debug)(
6609
- `Resolved Python entrypoint to "${detected.entrypoint.entrypoint}" (configured "${entrypoint}" not found).`
6610
- );
6611
- entrypoint = detected.entrypoint.entrypoint;
6612
- } else {
6613
- const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
6614
- entrypointNotFound = new import_build_utils14.NowBuildError({
6615
- code: `${framework.toUpperCase()}_ENTRYPOINT_NOT_FOUND`,
6616
- message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searchedList}.`,
6617
- link: `https://vercel.com/docs/frameworks/backend/${framework}#exporting-the-${framework}-application`,
6618
- action: "Learn More"
6619
- });
6620
- }
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
- }
6643
- if (entrypointNotFound && detected?.baseDir === void 0) {
6644
- throw entrypointNotFound;
6638
+ detected = await detectPythonEntrypoint(
6639
+ config.framework,
6640
+ workPath,
6641
+ entrypoint,
6642
+ service
6643
+ ) ?? void 0;
6644
+ if (detected?.error && detected?.baseDir === void 0) {
6645
+ throw detected?.error;
6645
6646
  }
6646
- const entryDirectory = detected?.baseDir ?? (0, import_path13.dirname)(entrypoint);
6647
+ const entryDirectory = detected?.baseDir ?? (entrypoint ? (0, import_path13.dirname)(entrypoint) : ".");
6647
6648
  const entrypointAbsDir = (0, import_path13.join)(workPath, entryDirectory);
6648
6649
  const rootDir = repoRootPath ?? workPath;
6649
6650
  const pythonPackage = await builderSpan.child("vc.builder.python.discover").trace(
@@ -6674,7 +6675,6 @@ var build = async ({
6674
6675
  `
6675
6676
  );
6676
6677
  }
6677
- fsFiles = await (0, import_build_utils14.glob)("**", workPath);
6678
6678
  const venvPath = service?.name ? (0, import_path13.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path13.join)(workPath, ".vercel", "python", ".venv");
6679
6679
  await builderSpan.child("vc.builder.python.venv").trace(async () => {
6680
6680
  await ensureVenv({
@@ -6682,14 +6682,14 @@ var build = async ({
6682
6682
  venvPath
6683
6683
  });
6684
6684
  });
6685
- if ((0, import_build_utils14.isPythonFramework)(framework)) {
6685
+ if ((0, import_build_utils15.isPythonFramework)(framework)) {
6686
6686
  const {
6687
6687
  cliType,
6688
6688
  lockfileVersion,
6689
6689
  packageJsonPackageManager,
6690
6690
  turboSupportsCorepackHome
6691
- } = await (0, import_build_utils14.scanParentDirs)(workPath, true);
6692
- spawnEnv = (0, import_build_utils14.getEnvForPackageManager)({
6691
+ } = await (0, import_build_utils15.scanParentDirs)(workPath, true);
6692
+ spawnEnv = (0, import_build_utils15.getEnvForPackageManager)({
6693
6693
  cliType,
6694
6694
  lockfileVersion,
6695
6695
  packageJsonPackageManager,
@@ -6728,14 +6728,14 @@ var build = async ({
6728
6728
  let uvLockPath = null;
6729
6729
  let uvProjectDir = null;
6730
6730
  let projectName;
6731
- await builderSpan.child(import_build_utils14.BUILDER_INSTALLER_STEP, {
6731
+ await builderSpan.child(import_build_utils15.BUILDER_INSTALLER_STEP, {
6732
6732
  installCommand: projectInstallCommand || void 0
6733
6733
  }).trace(async () => {
6734
6734
  if (projectInstallCommand) {
6735
6735
  console.log(
6736
6736
  `Running "install" command: \`${projectInstallCommand}\`...`
6737
6737
  );
6738
- await (0, import_build_utils14.execCommand)(projectInstallCommand, {
6738
+ await (0, import_build_utils15.execCommand)(projectInstallCommand, {
6739
6739
  env: pythonEnv,
6740
6740
  cwd: workPath
6741
6741
  });
@@ -6775,15 +6775,15 @@ var build = async ({
6775
6775
  });
6776
6776
  }
6777
6777
  });
6778
- if ((0, import_build_utils14.isPythonFramework)(framework)) {
6778
+ if ((0, import_build_utils15.isPythonFramework)(framework)) {
6779
6779
  const projectBuildCommand = config?.projectSettings?.buildCommand ?? // fallback if provided directly on config (some callers set this)
6780
6780
  config?.buildCommand;
6781
- await builderSpan.child(import_build_utils14.BUILDER_COMPILE_STEP, {
6781
+ await builderSpan.child(import_build_utils15.BUILDER_COMPILE_STEP, {
6782
6782
  buildCommand: projectBuildCommand || void 0
6783
6783
  }).trace(async () => {
6784
6784
  if (projectBuildCommand) {
6785
6785
  console.log(`Running "${projectBuildCommand}"`);
6786
- await (0, import_build_utils14.execCommand)(projectBuildCommand, {
6786
+ await (0, import_build_utils15.execCommand)(projectBuildCommand, {
6787
6787
  env: pythonEnv,
6788
6788
  cwd: workPath
6789
6789
  });
@@ -6805,16 +6805,19 @@ var build = async ({
6805
6805
  detected
6806
6806
  });
6807
6807
  const resolved = hookResult?.entrypoint ?? detected?.entrypoint;
6808
- if (entrypointNotFound && resolved) {
6809
- entrypoint = resolved.entrypoint;
6810
- entrypointNotFound = void 0;
6811
- }
6812
- if (entrypointNotFound) {
6813
- throw entrypointNotFound;
6808
+ if (!resolved && detected?.error) {
6809
+ throw detected?.error;
6810
+ }
6811
+ entrypoint = resolved?.entrypoint;
6812
+ if (!entrypoint) {
6813
+ throw new import_build_utils15.NowBuildError({
6814
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
6815
+ message: "No Python entrypoint could be detected. Please specify an entrypoint file."
6816
+ });
6814
6817
  }
6815
6818
  const djangoStatic = hookResult?.djangoStatic ?? null;
6816
6819
  const runtimeDep = baseEnv.VERCEL_RUNTIME_PYTHON || `vercel-runtime==${VERCEL_RUNTIME_VERSION}`;
6817
- (0, import_build_utils14.debug)(`Installing ${runtimeDep}`);
6820
+ (0, import_build_utils15.debug)(`Installing ${runtimeDep}`);
6818
6821
  await uv.pip({
6819
6822
  venvPath,
6820
6823
  projectDir: (0, import_path13.join)(workPath, entryDirectory),
@@ -6822,7 +6825,7 @@ var build = async ({
6822
6825
  });
6823
6826
  if (shouldInstallVercelWorkers) {
6824
6827
  const workersDep = baseEnv.VERCEL_WORKERS_PYTHON || `vercel-workers==${VERCEL_WORKERS_VERSION}`;
6825
- (0, import_build_utils14.debug)(`Installing ${workersDep}`);
6828
+ (0, import_build_utils15.debug)(`Installing ${workersDep}`);
6826
6829
  await uv.pip({
6827
6830
  venvPath,
6828
6831
  projectDir: (0, import_path13.join)(workPath, entryDirectory),
@@ -6833,7 +6836,7 @@ var build = async ({
6833
6836
  if (quirksResult.buildEnv) {
6834
6837
  Object.assign(pythonEnv, quirksResult.buildEnv);
6835
6838
  }
6836
- (0, import_build_utils14.debug)("Entrypoint is", entrypoint);
6839
+ (0, import_build_utils15.debug)("Entrypoint is", entrypoint);
6837
6840
  const moduleName = entrypoint.replace(/\//g, ".").replace(/\.py$/i, "");
6838
6841
  const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
6839
6842
  if (handlerFunction) {
@@ -6841,7 +6844,7 @@ var build = async ({
6841
6844
  const source = await import_fs11.default.promises.readFile(entrypointPath, "utf-8");
6842
6845
  const found = await (0, import_python_analysis9.containsTopLevelCallable)(source, handlerFunction);
6843
6846
  if (!found) {
6844
- throw new import_build_utils14.NowBuildError({
6847
+ throw new import_build_utils15.NowBuildError({
6845
6848
  code: "PYTHON_HANDLER_NOT_FOUND",
6846
6849
  message: `Handler function "${handlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
6847
6850
  });
@@ -6850,7 +6853,7 @@ var build = async ({
6850
6853
  const vendorDir = resolveVendorDir();
6851
6854
  const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
6852
6855
  const entrypointWithSuffix = `${entrypoint}${suffix}`;
6853
- (0, import_build_utils14.debug)("Entrypoint with suffix is", entrypointWithSuffix);
6856
+ (0, import_build_utils15.debug)("Entrypoint with suffix is", entrypointWithSuffix);
6854
6857
  const handlerFuncEnvLine = handlerFunction ? `
6855
6858
  "__VC_HANDLER_FUNC_NAME": "${handlerFunction}",` : "";
6856
6859
  const variableName = resolved?.variableName ?? "";
@@ -6911,18 +6914,6 @@ from vercel_runtime.vc_init import vc_handler
6911
6914
  "**/yarn.lock",
6912
6915
  "**/package-lock.json"
6913
6916
  ];
6914
- if (djangoStatic) {
6915
- const dirsToExclude = [
6916
- ...djangoStatic.staticSourceDirs,
6917
- ...djangoStatic.staticRoot ? [djangoStatic.staticRoot] : []
6918
- ];
6919
- for (const absDir of dirsToExclude) {
6920
- const rel = (0, import_path13.relative)(workPath, absDir);
6921
- if (!rel.startsWith("..")) {
6922
- predefinedExcludes.push(`${rel}/**`);
6923
- }
6924
- }
6925
- }
6926
6917
  const lambdaEnv = {};
6927
6918
  lambdaEnv.PYTHONPATH = vendorDir;
6928
6919
  Object.assign(lambdaEnv, quirksResult.env);
@@ -6933,9 +6924,9 @@ from vercel_runtime.vc_init import vc_handler
6933
6924
  cwd: workPath,
6934
6925
  ignore: config && typeof config.excludeFiles === "string" ? [...predefinedExcludes, config.excludeFiles] : predefinedExcludes
6935
6926
  };
6936
- const files = await (0, import_build_utils14.glob)("**", globOptions);
6927
+ const files = await (0, import_build_utils15.glob)("**", globOptions);
6937
6928
  if (djangoStatic?.manifestRelPath) {
6938
- files[djangoStatic.manifestRelPath] = new import_build_utils14.FileFsRef({
6929
+ files[djangoStatic.manifestRelPath] = new import_build_utils15.FileFsRef({
6939
6930
  fsPath: (0, import_path13.join)(workPath, djangoStatic.manifestRelPath)
6940
6931
  });
6941
6932
  }
@@ -6972,12 +6963,12 @@ from vercel_runtime.vc_init import vc_handler
6972
6963
  }
6973
6964
  });
6974
6965
  const handlerPyFilename = "vc__handler__python";
6975
- files[`${handlerPyFilename}.py`] = new import_build_utils14.FileBlob({ data: runtimeTrampoline });
6966
+ files[`${handlerPyFilename}.py`] = new import_build_utils15.FileBlob({ data: runtimeTrampoline });
6976
6967
  if (config.framework === "fasthtml") {
6977
6968
  const { SESSKEY = "" } = process.env;
6978
- files[".sesskey"] = new import_build_utils14.FileBlob({ data: `"${SESSKEY}"` });
6969
+ files[".sesskey"] = new import_build_utils15.FileBlob({ data: `"${SESSKEY}"` });
6979
6970
  }
6980
- const output = new import_build_utils14.Lambda({
6971
+ const output = new import_build_utils15.Lambda({
6981
6972
  files,
6982
6973
  handler: `${handlerPyFilename}.vc_handler`,
6983
6974
  runtime: pythonVersion.runtime,
@@ -6993,33 +6984,33 @@ from vercel_runtime.vc_init import vc_handler
6993
6984
  uvLockPath
6994
6985
  });
6995
6986
  } catch (err) {
6996
- (0, import_build_utils14.debug)(
6987
+ (0, import_build_utils15.debug)(
6997
6988
  `Failed to write project manifest: ${err instanceof Error ? err.message : String(err)}`
6998
6989
  );
6999
6990
  }
7000
6991
  }
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
- };
6992
+ if (!(0, import_build_utils15.isPythonFramework)(framework)) {
6993
+ return { resultVersion: 3, result: { output } };
7017
6994
  }
7018
- return { resultVersion: 3, result: { output } };
6995
+ const lambdaPath = service?.name ? `_svc/${service.name}/index` : "index";
6996
+ const staticFiles = djangoStatic?.cdnOutputDir ? await (0, import_build_utils15.glob)("**", { cwd: djangoStatic.cdnOutputDir }) : {};
6997
+ return {
6998
+ resultVersion: 2,
6999
+ result: {
7000
+ output: {
7001
+ [lambdaPath]: output,
7002
+ ...staticFiles
7003
+ },
7004
+ routes: [
7005
+ { handle: "filesystem" },
7006
+ { src: "/(.*)", dest: `/${lambdaPath}` }
7007
+ ]
7008
+ }
7009
+ };
7019
7010
  };
7020
7011
  var shouldServe = (opts) => {
7021
7012
  const framework = opts.config.framework;
7022
- if ((0, import_build_utils14.isPythonFramework)(framework)) {
7013
+ if ((0, import_build_utils15.isPythonFramework)(framework)) {
7023
7014
  const requestPath = opts.requestPath.replace(/\/$/, "");
7024
7015
  if (requestPath.startsWith("api") && opts.hasMatched) {
7025
7016
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.28.0",
3
+ "version": "6.29.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -36,9 +36,9 @@
36
36
  "smol-toml": "1.5.2",
37
37
  "vitest": "2.1.4",
38
38
  "which": "3.0.0",
39
- "@vercel/build-utils": "13.10.0",
40
- "@vercel/python-runtime": "0.11.0",
41
- "@vercel/error-utils": "2.0.3"
39
+ "@vercel/build-utils": "13.12.1",
40
+ "@vercel/error-utils": "2.0.3",
41
+ "@vercel/python-runtime": "0.12.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "node ../../utils/build-builder.mjs",
@@ -33,11 +33,14 @@ else:
33
33
 
34
34
  mod = importlib.import_module(settings_module)
35
35
  settings = {k: getattr(mod, k) for k in dir(mod) if k.isupper()}
36
+ import django
37
+
36
38
  print(
37
39
  json.dumps(
38
40
  {
39
41
  "settings_module": settings_module,
40
42
  "django_settings": settings,
43
+ "django_version": django.VERSION[:3],
41
44
  },
42
45
  default=str,
43
46
  )