@vercel/python 14.2.1 → 16.0.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 +802 -528
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -7078,6 +7078,7 @@ var UvRunner = class {
7078
7078
  frozen,
7079
7079
  noBuild,
7080
7080
  noInstallProject,
7081
+ onlyGroup,
7081
7082
  pythonPlatform
7082
7083
  } = options;
7083
7084
  const args = ["sync", "--active", "--no-dev", "--link-mode", "hardlink"];
@@ -7092,6 +7093,9 @@ var UvRunner = class {
7092
7093
  if (noInstallProject) {
7093
7094
  args.push("--no-install-project");
7094
7095
  }
7096
+ if (onlyGroup) {
7097
+ args.push("--only-group", onlyGroup);
7098
+ }
7095
7099
  if (pythonPlatform) {
7096
7100
  args.push("--python-platform", pythonPlatform);
7097
7101
  }
@@ -12935,6 +12939,7 @@ function getImportClosureOptions({
12935
12939
  }
12936
12940
 
12937
12941
  // src/index.ts
12942
+ var import_build_utils25 = require("@vercel/build-utils");
12938
12943
  var writeFile = import_fs20.default.promises.writeFile;
12939
12944
  var PYTHON_ENTRYPOINT_DOCS_URL2 = "https://vercel.com/docs/functions/runtimes/python#python-entrypoints";
12940
12945
  var version = -1;
@@ -13330,95 +13335,17 @@ async function installInjectedPackage({
13330
13335
  ]
13331
13336
  });
13332
13337
  }
13333
- var build = async ({
13338
+ async function setupPythonToolchain({
13339
+ builderSpan,
13334
13340
  workPath,
13335
- repoRootPath,
13336
- files: originalFiles,
13337
- entrypoint: rawEntrypoint,
13338
- meta = {},
13339
- config,
13340
- span: parentSpan,
13341
- service,
13342
- registerPreDeploy
13343
- }) => {
13344
- let entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
13345
- const isPyprojectEntrypoint = entrypoint !== void 0 && (0, import_path21.basename)(entrypoint) === "pyproject.toml";
13346
- if (isPyprojectEntrypoint && entrypoint !== "pyproject.toml") {
13347
- throw new import_build_utils24.NowBuildError({
13348
- code: "PYTHON_INVALID_PYPROJECT_ENTRYPOINT",
13349
- message: `A "pyproject.toml" entrypoint must sit at the service root. Set the service "root" to "${(0, import_path21.dirname)(entrypoint)}" and use entrypoint "pyproject.toml".`
13350
- });
13351
- }
13352
- const builderSpan = parentSpan ?? new import_build_utils24.Span({ name: "vc.builder" });
13353
- const framework = config?.framework;
13354
- let subscriberDeclarations = [];
13355
- let subscribers = [];
13356
- let workflows = [];
13357
- let legacyWorkersProject = false;
13358
- let workflowMode = "workers";
13359
- let spawnEnv;
13360
- let projectInstallCommand;
13361
- let hasCustomCommand = false;
13362
- const target = getTargetPlatform(meta.isDev ?? false);
13363
- (0, import_build_utils24.debug)(`workPath: ${workPath}`);
13364
- workPath = await downloadFilesInWorkPath({
13365
- workPath,
13366
- files: originalFiles,
13367
- entrypoint,
13368
- meta
13369
- });
13370
- legacyWorkersProject = await isLegacyWorkersProject(workPath);
13371
- if (isPyprojectEntrypoint || !service && (0, import_build_utils24.isPythonFramework)(framework)) {
13372
- subscriberDeclarations = await getPyprojectSubscribers(workPath, {
13373
- legacySchema: legacyWorkersProject
13374
- });
13375
- workflows = await getPyprojectWorkflows(workPath);
13376
- }
13377
- try {
13378
- if (meta.isDev) {
13379
- const setupCfg = (0, import_path21.join)(workPath, "setup.cfg");
13380
- await writeFile(setupCfg, "[install]\nprefix=\n");
13381
- }
13382
- } catch (err) {
13383
- console.log('Failed to create "setup.cfg" file');
13384
- throw err;
13385
- }
13386
- let detected;
13387
- const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
13388
- if (isPyprojectEntrypoint) {
13389
- const declared = await getVercelToolsEntrypoint(workPath, repoRootPath);
13390
- if (declared) {
13391
- detected = { entrypoint: declared };
13392
- } else if (subscriberDeclarations.length === 0 && workflows.length === 0) {
13393
- throw new import_build_utils24.NowBuildError({
13394
- code: "PYTHON_PYPROJECT_NOTHING_TO_BUILD",
13395
- message: 'Entrypoint "pyproject.toml" declares nothing to build. Set "tool.vercel.entrypoint" for a web app and/or declare "[[tool.vercel.subscribers]]" or "[[tool.vercel.workflows]]" entries in pyproject.toml.'
13396
- });
13397
- }
13398
- entrypoint = void 0;
13399
- } else {
13400
- detected = await detectPythonEntrypoint(
13401
- config.framework,
13402
- workPath,
13403
- entrypoint ? {
13404
- filePath: entrypoint,
13405
- // For schedule-triggered jobs, the WSGI variable is always 'app' (created dynamically).
13406
- // For other services, handlerFunction is used as the entrypoint variable name.
13407
- varName: service && (0, import_build_utils24.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
13408
- } : void 0,
13409
- service,
13410
- repoRootPath
13411
- ) ?? void 0;
13412
- }
13413
- if (detected?.error && detected?.baseDir === void 0) {
13414
- throw detected?.error;
13415
- }
13416
- const entryDirectory = detected?.baseDir ?? (entrypoint ? (0, import_path21.dirname)(entrypoint) : ".");
13417
- const entrypointAbsDir = (0, import_path21.join)(workPath, entryDirectory);
13418
- const rootDir = repoRootPath ?? workPath;
13341
+ entrypointDir,
13342
+ rootDir,
13343
+ venvPath,
13344
+ meta
13345
+ }) {
13419
13346
  const pythonPackage = await builderSpan.child("vc.builder.python.discover").trace(
13420
13347
  () => discoverPackage({
13421
- entrypointDir: entrypointAbsDir,
13348
+ entrypointDir,
13422
13349
  rootDir
13423
13350
  })
13424
13351
  );
@@ -13455,7 +13382,6 @@ var build = async ({
13455
13382
  }
13456
13383
  const uvVersion = checkUvBinaryVersion(uv.getPath());
13457
13384
  console.log(`Using ${uvVersion}`);
13458
- const venvPath = service?.name ? (0, import_path21.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path21.join)(workPath, ".vercel", "python", ".venv");
13459
13385
  const hasCachedVenv = import_fs20.default.existsSync((0, import_path21.join)(venvPath, "pyvenv.cfg"));
13460
13386
  const hasCachedUv = import_fs20.default.existsSync(uvCacheDir);
13461
13387
  const restoredCache = hasCachedVenv && hasCachedUv ? "both" : hasCachedVenv ? "venv" : hasCachedUv ? "uv" : "none";
@@ -13472,377 +13398,67 @@ var build = async ({
13472
13398
  uvCacheDir
13473
13399
  });
13474
13400
  });
13475
- if ((0, import_build_utils24.isPythonFramework)(framework)) {
13476
- const {
13477
- cliType,
13478
- lockfileVersion,
13479
- packageJsonPackageManager,
13480
- packageJsonDevEngines,
13481
- turboSupportsCorepackHome
13482
- } = await (0, import_build_utils24.scanParentDirs)(workPath, true);
13483
- spawnEnv = (0, import_build_utils24.getEnvForPackageManager)({
13484
- cliType,
13485
- lockfileVersion,
13486
- packageJsonPackageManager,
13487
- packageJsonDevEngines,
13488
- env: process.env,
13489
- turboSupportsCorepackHome,
13490
- projectCreatedAt: config?.projectSettings?.createdAt
13491
- });
13492
- const installCommand = config?.projectSettings?.installCommand;
13493
- if (typeof installCommand === "string") {
13494
- const trimmed = installCommand.trim();
13495
- if (trimmed) {
13496
- projectInstallCommand = trimmed;
13497
- } else {
13498
- console.log('Skipping "install" command...');
13499
- }
13500
- }
13501
- }
13502
- const baseEnv = spawnEnv || process.env;
13503
- const pythonEnv = createVenvEnv(venvPath, baseEnv, uvCacheDir);
13504
- pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
13505
- let assumeDepsInstalled = false;
13506
- let uvLockPath = null;
13507
- let uvProjectDir = null;
13508
- let projectName;
13509
- let usedUvManagedInstall = false;
13510
- await builderSpan.child(import_build_utils24.BUILDER_INSTALLER_STEP, {
13511
- installCommand: projectInstallCommand || void 0,
13512
- runtime: "python",
13513
- "python.cache.restored": restoredCache
13514
- }).trace(async () => {
13515
- if (projectInstallCommand) {
13516
- await import_fs20.default.promises.rm(venvPath, { recursive: true, force: true });
13517
- await ensureVenv({
13518
- pythonVersion,
13519
- venvPath,
13520
- uvPath: uv.getPath(),
13521
- uvCacheDir,
13522
- quiet: true
13523
- });
13524
- console.log(
13525
- `Running "install" command: \`${projectInstallCommand}\`...`
13526
- );
13527
- await (0, import_build_utils24.execCommand)(projectInstallCommand, {
13528
- env: pythonEnv,
13529
- cwd: workPath
13530
- });
13531
- assumeDepsInstalled = true;
13532
- hasCustomCommand = true;
13533
- } else {
13534
- const hasCustomScript = await runPyprojectScript(
13535
- workPath,
13536
- ["vercel-install", "now-install", "install"],
13537
- pythonEnv,
13538
- /* useUserVirtualEnv */
13539
- false
13540
- );
13541
- if (hasCustomScript) {
13542
- assumeDepsInstalled = true;
13543
- hasCustomCommand = true;
13544
- }
13545
- }
13546
- if (!assumeDepsInstalled) {
13547
- const lockCacheKey = service?.name ? `uv.lock.${service.name}` : "uv.lock";
13548
- const cachedLockPath = (0, import_path21.join)(uvCacheDir, lockCacheKey);
13549
- const { projectDir, lockPath, lockFileProvidedByUser } = await ensureUvProject({
13550
- workPath,
13551
- rootDir,
13552
- venvPath,
13553
- pythonPackage,
13554
- pythonVersion: pythonVersionString(pythonVersion),
13555
- uv,
13556
- requireBinaryWheels: false,
13557
- cachedLockPath
13558
- });
13559
- uvLockPath = lockPath;
13560
- uvProjectDir = projectDir;
13561
- projectName = pythonPackage?.manifest?.data?.project?.name;
13562
- await uv.sync({
13563
- venvPath,
13564
- projectDir,
13565
- frozen: lockFileProvidedByUser,
13566
- locked: !lockFileProvidedByUser,
13567
- pythonPlatform: target.uvPlatform
13568
- });
13569
- usedUvManagedInstall = true;
13570
- if (lockPath && import_fs20.default.existsSync(lockPath)) {
13571
- await import_fs20.default.promises.mkdir(uvCacheDir, { recursive: true });
13572
- await import_fs20.default.promises.copyFile(lockPath, cachedLockPath);
13573
- }
13574
- }
13575
- });
13576
- if ((0, import_build_utils24.isPythonFramework)(framework)) {
13577
- const projectBuildCommand = config?.projectSettings?.buildCommand ?? // fallback if provided directly on config (some callers set this)
13578
- config?.buildCommand;
13579
- await builderSpan.child(import_build_utils24.BUILDER_COMPILE_STEP, {
13580
- buildCommand: projectBuildCommand || void 0
13581
- }).trace(async () => {
13582
- if (projectBuildCommand) {
13583
- console.log(`Running "${projectBuildCommand}"`);
13584
- await (0, import_build_utils24.execCommand)(projectBuildCommand, {
13585
- env: pythonEnv,
13586
- cwd: workPath
13587
- });
13588
- } else {
13589
- await runPyprojectScript(
13590
- workPath,
13591
- ["vercel-build", "now-build", "build"],
13592
- pythonEnv
13593
- );
13594
- }
13595
- });
13596
- }
13597
- const hookResult = await runFrameworkHook(framework, {
13598
- pythonEnv,
13401
+ return { pythonPackage, pythonVersion, uv, uvCacheDir, restoredCache };
13402
+ }
13403
+ async function runUvManagedInstall({
13404
+ workPath,
13405
+ rootDir,
13406
+ venvPath,
13407
+ pythonPackage,
13408
+ pythonVersion,
13409
+ uv,
13410
+ uvCacheDir,
13411
+ lockCacheKey,
13412
+ pythonPlatform,
13413
+ extraSyncParams
13414
+ }) {
13415
+ const cachedLockPath = (0, import_path21.join)(uvCacheDir, lockCacheKey);
13416
+ const result = await ensureUvProject({
13599
13417
  workPath,
13418
+ rootDir,
13600
13419
  venvPath,
13601
- entrypoint,
13602
- detected,
13603
- pyprojectData: pythonPackage.manifest?.data
13604
- });
13605
- const resolved = isPyprojectEntrypoint ? detected?.entrypoint : hookResult?.entrypoint ?? detected?.entrypoint;
13606
- if (!resolved && detected?.error) {
13607
- throw detected?.error;
13608
- }
13609
- entrypoint = resolved?.entrypoint;
13610
- const isWorkersOnly = !entrypoint && isPyprojectEntrypoint && (subscriberDeclarations.length > 0 || workflows.length > 0);
13611
- if (!entrypoint && !isWorkersOnly) {
13612
- throw new import_build_utils24.NowBuildError({
13613
- code: "PYTHON_ENTRYPOINT_NOT_FOUND",
13614
- message: "No Python entrypoint could be detected. Please specify an entrypoint file."
13615
- });
13616
- }
13617
- const djangoStatic = hookResult?.djangoStatic ?? null;
13618
- const fastapiStatic = hookResult?.fastapiStatic ?? null;
13619
- const importSeeds = hookResult?.importSeeds ?? [];
13620
- const cdnOutputDir = djangoStatic?.cdnOutputDir ?? fastapiStatic?.cdnOutputDir ?? null;
13621
- const pipPlatformArgs = target.uvPlatform ? ["--python-platform", target.uvPlatform] : [];
13622
- await installInjectedPackage({
13623
- name: "vercel-runtime",
13624
- requirement: `vercel-runtime==${VERCEL_RUNTIME_VERSION}`,
13625
- envOverride: baseEnv.VERCEL_RUNTIME_PYTHON,
13626
- allowLocalSource: true,
13627
- uv,
13628
- venvPath,
13629
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13630
- pipPlatformArgs
13631
- });
13632
- const queueAdapterBootstrap = await getQueueAdapterBootstrap({
13633
13420
  pythonPackage,
13634
- env: baseEnv,
13635
- legacyWorkersProject,
13636
- hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
13421
+ pythonVersion: pythonVersionString(pythonVersion),
13422
+ uv,
13423
+ requireBinaryWheels: false,
13424
+ cachedLockPath
13637
13425
  });
13638
- const queueAdapterInjectedPackages = usedUvManagedInstall ? queueAdapterBootstrap.injectedPackages : [];
13639
- for (const injectedPackage of queueAdapterInjectedPackages) {
13640
- await installInjectedPackage({
13641
- ...injectedPackage,
13642
- uv,
13426
+ const { projectDir, lockPath, lockFileProvidedByUser } = result;
13427
+ if (extraSyncParams !== void 0) {
13428
+ await uv.sync({
13643
13429
  venvPath,
13644
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13645
- pipPlatformArgs
13430
+ projectDir,
13431
+ frozen: lockFileProvidedByUser,
13432
+ locked: !lockFileProvidedByUser,
13433
+ pythonPlatform,
13434
+ ...extraSyncParams
13646
13435
  });
13647
13436
  }
13648
- const localSdkSourcePaths = await getLocalSdkSourcePaths({
13649
- pythonPackage,
13650
- env: baseEnv
13651
- });
13652
- if (localSdkSourcePaths.length > 0) {
13653
- (0, import_build_utils24.debug)(
13654
- `Installing vercel SDK override from ${baseEnv.VERCEL_SDK_PYTHON}: ` + localSdkSourcePaths.join(", ")
13655
- );
13656
- await uv.pip({
13657
- venvPath,
13658
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13659
- args: [
13660
- "install",
13661
- "--link-mode",
13662
- "copy",
13663
- // Without --no-sources, uv resolves the checkout's workspace
13664
- // dependencies as editable .pth installs pointing at the (build
13665
- // only) checkout mount — invisible to bundling and dangling at
13666
- // runtime. Explicit paths stay real installs; the remaining
13667
- // workspace siblings resolve from PyPI.
13668
- "--no-sources",
13669
- ...pipPlatformArgs,
13670
- ...localSdkSourcePaths
13671
- ]
13672
- });
13673
- }
13674
- if (workflows.length > 0) {
13675
- workflowMode = await resolveWorkflowServingMode({
13676
- pythonPackage,
13677
- uv,
13678
- venvPath,
13679
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13680
- uvLockPath
13681
- });
13682
- if (workflowMode === "workers" && workflows.length > 1) {
13683
- throw new import_build_utils24.NowBuildError({
13684
- code: "PYTHON_INVALID_WORKFLOW_CONFIG",
13685
- message: `"tool.vercel.workflows" declares multiple entrypoints, which requires ${QUEUE_WORKFLOW_SDK_REQUIREMENT} with a distinct namespace per Workflows registry; the installed SDK serves every workflow on the shared __wkf_* topic`
13686
- });
13687
- }
13688
- }
13689
- const shouldInstallVercelWorkers = legacyWorkersProject || workflows.length > 0 && workflowMode === "workers";
13690
- if (shouldInstallVercelWorkers) {
13691
- await installInjectedPackage({
13692
- name: "vercel-workers",
13693
- requirement: `vercel-workers==${VERCEL_WORKERS_VERSION}`,
13694
- envOverride: baseEnv.VERCEL_WORKERS_PYTHON,
13695
- allowLocalSource: true,
13696
- uv,
13697
- venvPath,
13698
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13699
- pipPlatformArgs
13700
- });
13701
- }
13702
- const quirksResult = await runQuirks({ venvPath, pythonEnv, workPath });
13703
- if (quirksResult.buildEnv) {
13704
- Object.assign(pythonEnv, quirksResult.buildEnv);
13705
- }
13706
- const queueIntegrations = queueAdapterBootstrap.integrations;
13707
- const writeGeneratedQueueHandler = async (outputPath, declaration) => {
13708
- const generatedPath = getGeneratedQueueHandlerPath(outputPath);
13709
- await import_fs20.default.promises.mkdir((0, import_path21.dirname)((0, import_path21.join)(workPath, generatedPath)), {
13710
- recursive: true
13711
- });
13712
- await import_fs20.default.promises.writeFile(
13713
- (0, import_path21.join)(workPath, generatedPath),
13714
- createQueueHandlerModule(declaration, queueIntegrations)
13715
- );
13716
- };
13717
- if (subscriberDeclarations.length > 0 && !legacyWorkersProject) {
13718
- subscribers = await resolveQueueSubscribers({
13719
- declarations: subscriberDeclarations,
13720
- uv,
13721
- venvPath,
13722
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13723
- integrations: queueIntegrations
13724
- });
13725
- if (workflowMode === "queue") {
13726
- for (const subscriber of subscribers) {
13727
- if (!subscriber.topicPatterns) {
13728
- subscriber.subscriptions = subscriber.subscriptions.filter(
13729
- (subscription) => !isWorkflowQueueTopic(subscription.topic)
13730
- );
13731
- }
13732
- }
13733
- }
13734
- for (const subscriber of subscribers) {
13735
- await writeGeneratedQueueHandler(
13736
- getSubscriberOutputPath(subscriber.name),
13737
- subscriber
13738
- );
13739
- }
13740
- }
13741
- const workflowQueueSubscriptions = /* @__PURE__ */ new Map();
13742
- if (workflows.length > 0 && workflowMode === "queue") {
13743
- const resolved2 = await resolveQueueSubscribers({
13744
- declarations: workflows.map((workflow) => ({
13745
- name: workflow.name,
13746
- entrypoint: workflow.entrypoint,
13747
- moduleName: workflow.moduleName,
13748
- variableName: workflow.variableName
13749
- })),
13750
- uv,
13751
- venvPath,
13752
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13753
- kind: "workflow",
13754
- integrations: queueIntegrations
13755
- });
13756
- for (let i = 0; i < resolved2.length; i++) {
13757
- for (let j = i + 1; j < resolved2.length; j++) {
13758
- const overlaps = resolved2[i].subscriptions.some(
13759
- (left) => resolved2[j].subscriptions.some(
13760
- (right) => queueTopicPatternsOverlap(left.topic, right.topic)
13761
- )
13762
- );
13763
- if (overlaps) {
13764
- throw new import_build_utils24.NowBuildError({
13765
- code: "PYTHON_INVALID_WORKFLOW_CONFIG",
13766
- message: `workflow entrypoints "${resolved2[i].moduleName}:${resolved2[i].variableName}" and "${resolved2[j].moduleName}:${resolved2[j].variableName}" subscribe to overlapping queue topics; construct each vercel.workflow.Workflows registry with a distinct namespace`
13767
- });
13768
- }
13769
- }
13770
- }
13771
- for (const workflow of resolved2) {
13772
- workflowQueueSubscriptions.set(workflow.name, workflow.subscriptions);
13773
- await writeGeneratedQueueHandler(
13774
- getWorkflowOutputPath(workflow.name),
13775
- workflow
13776
- );
13777
- }
13778
- }
13779
- const preDeployCommand = config?.preDeployCommand;
13780
- if (registerPreDeploy && typeof preDeployCommand === "string") {
13781
- const capturedEnv = { ...pythonEnv };
13782
- const capturedCwd = workPath;
13783
- registerPreDeploy(async () => {
13784
- await builderSpan.child(import_build_utils24.BUILDER_PRE_DEPLOY_STEP, {
13785
- preDeployCommand
13786
- }).trace(async () => {
13787
- console.log(`Running pre-deploy command: \`${preDeployCommand}\``);
13788
- await (0, import_build_utils24.execCommand)(preDeployCommand, {
13789
- env: capturedEnv,
13790
- cwd: capturedCwd
13791
- });
13792
- });
13793
- });
13437
+ if (lockPath && import_fs20.default.existsSync(lockPath)) {
13438
+ await import_fs20.default.promises.mkdir(uvCacheDir, { recursive: true });
13439
+ await import_fs20.default.promises.copyFile(lockPath, cachedLockPath);
13794
13440
  }
13441
+ return result;
13442
+ }
13443
+ async function packFunctionBundle({
13444
+ builderSpan,
13445
+ workPath,
13446
+ uvLockPath,
13447
+ uvProjectDir,
13448
+ projectName,
13449
+ pythonVersion,
13450
+ hasCustomCommand,
13451
+ alwaysBundlePackages,
13452
+ initialFiles,
13453
+ additionalExcludes,
13454
+ compileAllEnabled,
13455
+ compileEnv,
13456
+ venvPath,
13457
+ entrypoint,
13458
+ importClosureContext,
13459
+ largeFunctionLabel
13460
+ }) {
13795
13461
  const vendorDir = resolveVendorDir();
13796
- let crons;
13797
- let runtimeTrampoline;
13798
- if (entrypoint) {
13799
- (0, import_build_utils24.debug)("Entrypoint is", entrypoint);
13800
- const moduleName = entrypointToModule(entrypoint);
13801
- if (handlerFunction) {
13802
- const entrypointPath = (0, import_path21.join)(workPath, entrypoint);
13803
- const source = await import_fs20.default.promises.readFile(entrypointPath, "utf-8");
13804
- const found = await (0, import_python_analysis12.containsTopLevelCallable)(source, handlerFunction);
13805
- if (!found) {
13806
- throw new import_build_utils24.NowBuildError({
13807
- code: "PYTHON_HANDLER_NOT_FOUND",
13808
- message: `Handler function "${handlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
13809
- });
13810
- }
13811
- }
13812
- const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
13813
- const entrypointWithSuffix = `${entrypoint}${suffix}`;
13814
- (0, import_build_utils24.debug)("Entrypoint with suffix is", entrypointWithSuffix);
13815
- crons = await getServiceCrons({
13816
- service,
13817
- entrypoint,
13818
- rawEntrypoint,
13819
- handlerFunction,
13820
- pythonBin: getVenvPythonBin(venvPath),
13821
- env: pythonEnv,
13822
- workPath
13823
- });
13824
- const extraTrampolineEnv = [];
13825
- if (crons?.length) {
13826
- const json = JSON.stringify(buildCronRouteTable(crons));
13827
- (0, import_assert.default)(!json.includes("\\"), `backslash in cron route table: ${json}`);
13828
- extraTrampolineEnv.push(`"__VC_CRON_ROUTES": '${json}'`);
13829
- }
13830
- const variableName = resolved?.variableName ?? "";
13831
- runtimeTrampoline = createRuntimeTrampoline({
13832
- moduleName,
13833
- entrypoint: entrypointWithSuffix,
13834
- vendorDir,
13835
- variableName,
13836
- extraEnv: extraTrampolineEnv
13837
- });
13838
- }
13839
- const compileAllEnabled = shouldCompileAll({
13840
- isDev: meta.isDev,
13841
- hasCustomCommand,
13842
- // A pre-deploy command can rewrite source after the build, which would make
13843
- // unchecked-hash precompiled bytecode stale; skip precompilation to avoid serving it.
13844
- hasPreDeployCommand: typeof preDeployCommand === "string"
13845
- });
13846
13462
  const predefinedExcludes = [
13847
13463
  ".git/**",
13848
13464
  ".gitignore",
@@ -13861,71 +13477,13 @@ var build = async ({
13861
13477
  "**/yarn.lock",
13862
13478
  "**/package-lock.json"
13863
13479
  ];
13864
- const lambdaEnv = {};
13865
- lambdaEnv.PYTHONPATH = vendorDir;
13866
- lambdaEnv.PYTHONDONTWRITEBYTECODE = "1";
13867
- Object.assign(lambdaEnv, quirksResult.env);
13868
- if (shouldInstallVercelWorkers) {
13869
- lambdaEnv.VERCEL_HAS_WORKER_SERVICES = "1";
13870
- }
13871
- if (queueIntegrations.length > 0) {
13872
- lambdaEnv.VERCEL_QUEUE_INTEGRATIONS = queueIntegrations.map(
13873
- ({ module: module2, installer, servingActivator }) => `${module2}:${installer}` + (servingActivator ? `:${servingActivator}` : "")
13874
- ).join(",");
13875
- }
13876
- const apschedulerSubscribers = apschedulerSubscriberIdentities(subscribers);
13877
- if (apschedulerSubscribers.length > 0) {
13878
- lambdaEnv.VERCEL_APSCHEDULER_SUBSCRIBERS = JSON.stringify(
13879
- apschedulerSubscribers
13880
- );
13881
- const previewConfig = await getApschedulerPreviewConfig(workPath);
13882
- if (previewConfig) {
13883
- lambdaEnv.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS = String(
13884
- previewConfig.idleTimeoutSeconds
13885
- );
13886
- }
13887
- }
13888
- const functionConfig = getMatchingFunction(
13889
- config,
13890
- entrypoint
13891
- )?.functionConfig;
13892
- const includeFilesPatterns = [
13893
- ...normalizeGlobs(config?.includeFiles),
13894
- ...normalizeGlobs(functionConfig?.includeFiles)
13895
- ];
13896
- const excludeFilesPatterns = [
13897
- ...normalizeGlobs(config?.excludeFiles),
13898
- ...normalizeGlobs(functionConfig?.excludeFiles)
13899
- ];
13900
- const cdnBundle = getFastAPICdnBundle(
13901
- fastapiStatic?.collectedMounts ?? [],
13902
- workPath,
13903
- pythonPackage.manifest?.data?.tool?.vercel?.fastapi
13904
- );
13905
- const globOptions = {
13480
+ const files = await (0, import_build_utils24.glob)("**", {
13906
13481
  cwd: workPath,
13907
- ignore: [
13908
- ...predefinedExcludes,
13909
- ...excludeFilesPatterns,
13910
- ...cdnBundle.excludePatterns
13911
- ]
13912
- };
13913
- const files = await (0, import_build_utils24.glob)("**", globOptions);
13914
- for (const pattern of includeFilesPatterns) {
13915
- Object.assign(files, await (0, import_build_utils24.glob)(pattern, workPath));
13916
- }
13917
- Object.assign(files, cdnBundle.directoryStubs);
13482
+ ignore: [...predefinedExcludes, ...additionalExcludes ?? []]
13483
+ });
13484
+ Object.assign(files, initialFiles);
13918
13485
  const appPythonSourceFiles = Object.keys(files).filter((file) => file.endsWith(".py")).map((file) => (0, import_path21.join)(workPath, file)).sort();
13919
- if (djangoStatic?.manifestRelPath) {
13920
- files[djangoStatic.manifestRelPath] = new import_build_utils24.FileFsRef({
13921
- fsPath: (0, import_path21.join)(workPath, djangoStatic.manifestRelPath)
13922
- });
13923
- }
13924
- const handlerPyFilename = "vc__handler__python";
13925
- if (config.framework === "fasthtml") {
13926
- const { SESSKEY = "" } = process.env;
13927
- files[".sesskey"] = new import_build_utils24.FileBlob({ data: `"${SESSKEY}"` });
13928
- }
13486
+ let pycachePrefix;
13929
13487
  await builderSpan.child("vc.builder.python.bundle").trace(async (bundleSpan) => {
13930
13488
  const installedDistributions = await InstalledPythonDistributions.load({
13931
13489
  venvPath,
@@ -13941,10 +13499,7 @@ var build = async ({
13941
13499
  projectName,
13942
13500
  pythonVersion,
13943
13501
  hasCustomCommand,
13944
- alwaysBundlePackages: [
13945
- ...quirksResult.alwaysBundlePackages ?? [],
13946
- ...shouldInstallVercelWorkers ? ["vercel-workers", "vercel_workers"] : []
13947
- ]
13502
+ alwaysBundlePackages
13948
13503
  });
13949
13504
  const depAnalysis = await depExternalizer.analyze(files, {
13950
13505
  onSized: ({ totalSizeBytes, runtimeInstallEnabled }) => {
@@ -13958,11 +13513,11 @@ var build = async ({
13958
13513
  });
13959
13514
  const compileAllOptions = compileAllEnabled ? {
13960
13515
  pythonBin: getVenvPythonBin(venvPath),
13961
- env: pythonEnv
13516
+ env: compileEnv
13962
13517
  } : null;
13963
13518
  const compileSources = async ({
13964
13519
  includePackages,
13965
- pycachePrefix
13520
+ pycachePrefix: pycachePrefixArg
13966
13521
  }) => {
13967
13522
  if (!compileAllOptions)
13968
13523
  return void 0;
@@ -13973,7 +13528,7 @@ var build = async ({
13973
13528
  const result = await runCompileAll({
13974
13529
  ...compileAllOptions,
13975
13530
  sourceFiles: [...appPythonSourceFiles, ...vendorSourceFiles],
13976
- pycachePrefix
13531
+ pycachePrefix: pycachePrefixArg
13977
13532
  });
13978
13533
  timings = result.timings;
13979
13534
  compileSpan.setAttributes({
@@ -13998,13 +13553,8 @@ var build = async ({
13998
13553
  getImportClosureOptions({
13999
13554
  workPath,
14000
13555
  entrypoint,
14001
- frameworkSeeds: importSeeds,
14002
- extraPythonPath: hookResult?.extraPythonPath,
14003
- subscriberDeclarations,
14004
- subscribers,
14005
- workflows,
14006
- workflowMode,
14007
- sitePackageDirs
13556
+ sitePackageDirs,
13557
+ ...importClosureContext
14008
13558
  })
14009
13559
  ),
14010
13560
  IMPORT_CLOSURE_TIMEOUT_MS,
@@ -14158,7 +13708,7 @@ var build = async ({
14158
13708
  timings
14159
13709
  });
14160
13710
  if (bytesAdded > 0) {
14161
- lambdaEnv.PYTHONPYCACHEPREFIX = RUNTIME_PYCACHE_PREFIX;
13711
+ pycachePrefix = RUNTIME_PYCACHE_PREFIX;
14162
13712
  }
14163
13713
  } catch (err) {
14164
13714
  console.log(
@@ -14168,7 +13718,7 @@ var build = async ({
14168
13718
  }
14169
13719
  };
14170
13720
  const announceLargeFunction = () => console.log(
14171
- `Function "${entrypoint ?? rawEntrypoint}" exceeds the standard size limit; enabling large functions (beta).`
13721
+ `Function "${largeFunctionLabel}" exceeds the standard size limit; enabling large functions (beta).`
14172
13722
  );
14173
13723
  let packingMode;
14174
13724
  let runtimeToolingBytes = 0;
@@ -14235,6 +13785,730 @@ var build = async ({
14235
13785
  "python.bundle.packingMode": packingMode
14236
13786
  });
14237
13787
  });
13788
+ return { pycachePrefix, vendorDir, files };
13789
+ }
13790
+ async function buildProxyMiddleware({
13791
+ workPath,
13792
+ repoRootPath,
13793
+ files: originalFiles,
13794
+ entrypoint,
13795
+ config,
13796
+ meta,
13797
+ builderSpan
13798
+ }) {
13799
+ const target = getTargetPlatform(meta.isDev ?? false);
13800
+ (0, import_build_utils24.debug)(`workPath: ${workPath}`);
13801
+ workPath = await downloadFilesInWorkPath({
13802
+ workPath,
13803
+ files: originalFiles,
13804
+ entrypoint,
13805
+ meta
13806
+ });
13807
+ const proxyHandlerFunction = config.handlerFunction;
13808
+ if (typeof proxyHandlerFunction !== "string" || !proxyHandlerFunction) {
13809
+ throw new Error(
13810
+ 'Python proxy build requires "config.handlerFunction" to be set'
13811
+ );
13812
+ }
13813
+ const proxySource = await import_fs20.default.promises.readFile(
13814
+ (0, import_path21.join)(workPath, entrypoint),
13815
+ "utf-8"
13816
+ );
13817
+ if (!await (0, import_python_analysis12.containsTopLevelCallable)(proxySource, proxyHandlerFunction)) {
13818
+ throw new import_build_utils24.NowBuildError({
13819
+ code: "PYTHON_HANDLER_NOT_FOUND",
13820
+ message: `Handler function "${proxyHandlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
13821
+ });
13822
+ }
13823
+ const rootDir = repoRootPath ?? workPath;
13824
+ const venvPath = (0, import_path21.join)(workPath, ".vercel", "python", "vercel.proxy", ".venv");
13825
+ const { pythonPackage, pythonVersion, uv, uvCacheDir, restoredCache } = await setupPythonToolchain({
13826
+ builderSpan,
13827
+ workPath,
13828
+ entrypointDir: (0, import_path21.join)(workPath, (0, import_path21.dirname)(entrypoint)),
13829
+ rootDir,
13830
+ venvPath,
13831
+ meta
13832
+ });
13833
+ const { projectDir, lockPath: uvLockPath } = await builderSpan.child(import_build_utils24.BUILDER_INSTALLER_STEP, {
13834
+ runtime: "python",
13835
+ "python.cache.restored": restoredCache
13836
+ }).trace(
13837
+ () => runUvManagedInstall({
13838
+ workPath,
13839
+ rootDir,
13840
+ venvPath,
13841
+ pythonPackage,
13842
+ pythonVersion,
13843
+ uv,
13844
+ uvCacheDir,
13845
+ lockCacheKey: "uv.lock",
13846
+ pythonPlatform: target.uvPlatform,
13847
+ // Install only the proxy's dependency group into the proxy venv;
13848
+ // the project itself is neither built nor installed.
13849
+ // If the group is absent the proxy has no extra deps — skip sync.
13850
+ extraSyncParams: "vercel.proxy" in (pythonPackage?.manifest?.data?.["dependency-groups"] ?? {}) ? { onlyGroup: "vercel.proxy", noInstallProject: true } : void 0
13851
+ })
13852
+ );
13853
+ const pipPlatformArgs = target.uvPlatform ? ["--python-platform", target.uvPlatform] : [];
13854
+ await installInjectedPackage({
13855
+ name: "vercel-runtime",
13856
+ requirement: `vercel-runtime==${VERCEL_RUNTIME_VERSION}`,
13857
+ envOverride: process.env.VERCEL_RUNTIME_PYTHON,
13858
+ allowLocalSource: true,
13859
+ uv,
13860
+ venvPath,
13861
+ projectDir,
13862
+ pipPlatformArgs
13863
+ });
13864
+ const projectName = pythonPackage?.manifest?.data?.project?.name;
13865
+ const compileAllEnabled = shouldCompileAll({
13866
+ isDev: meta.isDev ?? false,
13867
+ hasCustomCommand: false
13868
+ });
13869
+ const pythonEnv = createVenvEnv(venvPath, process.env, uvCacheDir);
13870
+ const functionConfig = getMatchingFunction(
13871
+ config,
13872
+ entrypoint
13873
+ )?.functionConfig;
13874
+ const includeFilesPatterns = [
13875
+ ...normalizeGlobs(config?.includeFiles),
13876
+ ...normalizeGlobs(functionConfig?.includeFiles)
13877
+ ];
13878
+ const excludeFilesPatterns = [
13879
+ ...normalizeGlobs(config?.excludeFiles),
13880
+ ...normalizeGlobs(functionConfig?.excludeFiles)
13881
+ ];
13882
+ const initialFiles = {};
13883
+ for (const pattern of includeFilesPatterns) {
13884
+ Object.assign(initialFiles, await (0, import_build_utils24.glob)(pattern, workPath));
13885
+ }
13886
+ const { pycachePrefix, vendorDir, files } = await packFunctionBundle({
13887
+ builderSpan,
13888
+ workPath,
13889
+ uvLockPath,
13890
+ uvProjectDir: projectDir,
13891
+ projectName,
13892
+ pythonVersion,
13893
+ hasCustomCommand: false,
13894
+ alwaysBundlePackages: [],
13895
+ initialFiles,
13896
+ additionalExcludes: excludeFilesPatterns,
13897
+ compileAllEnabled,
13898
+ compileEnv: pythonEnv,
13899
+ venvPath,
13900
+ entrypoint,
13901
+ importClosureContext: {
13902
+ frameworkSeeds: [],
13903
+ extraPythonPath: void 0,
13904
+ subscriberDeclarations: [],
13905
+ subscribers: [],
13906
+ workflows: [],
13907
+ workflowMode: "workers"
13908
+ },
13909
+ largeFunctionLabel: entrypoint
13910
+ });
13911
+ const moduleName = entrypointToModule(entrypoint);
13912
+ const variableName = proxyHandlerFunction;
13913
+ const handlerPyFilename = "vc__handler__python";
13914
+ const runtimeTrampoline = createRuntimeTrampoline({
13915
+ moduleName,
13916
+ entrypoint,
13917
+ vendorDir,
13918
+ variableName,
13919
+ extraEnv: []
13920
+ });
13921
+ files[`${handlerPyFilename}.py`] = new import_build_utils24.FileBlob({ data: runtimeTrampoline });
13922
+ const lambdaOptions = await getPythonLambdaOptions({ config, entrypoint });
13923
+ const output = new import_build_utils24.Lambda({
13924
+ files,
13925
+ handler: `${handlerPyFilename}.vc_handler`,
13926
+ runtime: pythonVersion.runtime,
13927
+ ...lambdaOptions,
13928
+ architecture: target.architecture,
13929
+ environment: {
13930
+ PYTHONPATH: vendorDir,
13931
+ PYTHONDONTWRITEBYTECODE: "1",
13932
+ ...pycachePrefix ? { PYTHONPYCACHEPREFIX: pycachePrefix } : {}
13933
+ },
13934
+ supportsResponseStreaming: true
13935
+ });
13936
+ const matcher = (0, import_build_utils25.resolveMiddlewareMatcher)(
13937
+ config.middlewareMatcher,
13938
+ void 0,
13939
+ entrypoint
13940
+ );
13941
+ const src = (0, import_build_utils25.getRegExpFromMatchers)(matcher);
13942
+ const middlewareRawSrc = !matcher ? [] : Array.isArray(matcher) ? [...matcher] : [matcher];
13943
+ return {
13944
+ resultVersion: 3,
13945
+ result: {
13946
+ output,
13947
+ routes: [
13948
+ {
13949
+ src,
13950
+ middlewareRawSrc,
13951
+ middlewarePath: entrypoint.replace(/\.py$/i, ""),
13952
+ continue: true,
13953
+ override: true
13954
+ }
13955
+ ]
13956
+ }
13957
+ };
13958
+ }
13959
+ var build = async ({
13960
+ workPath,
13961
+ repoRootPath,
13962
+ files: originalFiles,
13963
+ entrypoint: rawEntrypoint,
13964
+ meta = {},
13965
+ config,
13966
+ span: parentSpan,
13967
+ service,
13968
+ registerPreDeploy
13969
+ }) => {
13970
+ let entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
13971
+ const builderSpan = parentSpan ?? new import_build_utils24.Span({ name: "vc.builder" });
13972
+ if (config?.middleware === true && entrypoint) {
13973
+ return buildProxyMiddleware({
13974
+ workPath,
13975
+ repoRootPath,
13976
+ files: originalFiles,
13977
+ entrypoint,
13978
+ config,
13979
+ meta,
13980
+ builderSpan
13981
+ });
13982
+ }
13983
+ const isPyprojectEntrypoint = entrypoint !== void 0 && (0, import_path21.basename)(entrypoint) === "pyproject.toml";
13984
+ if (isPyprojectEntrypoint && entrypoint !== "pyproject.toml") {
13985
+ throw new import_build_utils24.NowBuildError({
13986
+ code: "PYTHON_INVALID_PYPROJECT_ENTRYPOINT",
13987
+ message: `A "pyproject.toml" entrypoint must sit at the service root. Set the service "root" to "${(0, import_path21.dirname)(entrypoint)}" and use entrypoint "pyproject.toml".`
13988
+ });
13989
+ }
13990
+ const framework = config?.framework;
13991
+ let subscriberDeclarations = [];
13992
+ let subscribers = [];
13993
+ let workflows = [];
13994
+ let legacyWorkersProject = false;
13995
+ let workflowMode = "workers";
13996
+ let spawnEnv;
13997
+ let projectInstallCommand;
13998
+ let hasCustomCommand = false;
13999
+ const target = getTargetPlatform(meta.isDev ?? false);
14000
+ (0, import_build_utils24.debug)(`workPath: ${workPath}`);
14001
+ workPath = await downloadFilesInWorkPath({
14002
+ workPath,
14003
+ files: originalFiles,
14004
+ entrypoint,
14005
+ meta
14006
+ });
14007
+ legacyWorkersProject = await isLegacyWorkersProject(workPath);
14008
+ if (isPyprojectEntrypoint || !service && (0, import_build_utils24.isPythonFramework)(framework)) {
14009
+ subscriberDeclarations = await getPyprojectSubscribers(workPath, {
14010
+ legacySchema: legacyWorkersProject
14011
+ });
14012
+ workflows = await getPyprojectWorkflows(workPath);
14013
+ }
14014
+ try {
14015
+ if (meta.isDev) {
14016
+ const setupCfg = (0, import_path21.join)(workPath, "setup.cfg");
14017
+ await writeFile(setupCfg, "[install]\nprefix=\n");
14018
+ }
14019
+ } catch (err) {
14020
+ console.log('Failed to create "setup.cfg" file');
14021
+ throw err;
14022
+ }
14023
+ let detected;
14024
+ const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
14025
+ if (isPyprojectEntrypoint) {
14026
+ const declared = await getVercelToolsEntrypoint(workPath, repoRootPath);
14027
+ if (declared) {
14028
+ detected = { entrypoint: declared };
14029
+ } else if (subscriberDeclarations.length === 0 && workflows.length === 0) {
14030
+ throw new import_build_utils24.NowBuildError({
14031
+ code: "PYTHON_PYPROJECT_NOTHING_TO_BUILD",
14032
+ message: 'Entrypoint "pyproject.toml" declares nothing to build. Set "tool.vercel.entrypoint" for a web app and/or declare "[[tool.vercel.subscribers]]" or "[[tool.vercel.workflows]]" entries in pyproject.toml.'
14033
+ });
14034
+ }
14035
+ entrypoint = void 0;
14036
+ } else {
14037
+ detected = await detectPythonEntrypoint(
14038
+ config.framework,
14039
+ workPath,
14040
+ entrypoint ? {
14041
+ filePath: entrypoint,
14042
+ // For schedule-triggered jobs, the WSGI variable is always 'app' (created dynamically).
14043
+ // For other services, handlerFunction is used as the entrypoint variable name.
14044
+ varName: service && (0, import_build_utils24.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
14045
+ } : void 0,
14046
+ service,
14047
+ repoRootPath
14048
+ ) ?? void 0;
14049
+ }
14050
+ if (detected?.error && detected?.baseDir === void 0) {
14051
+ throw detected?.error;
14052
+ }
14053
+ const entryDirectory = detected?.baseDir ?? (entrypoint ? (0, import_path21.dirname)(entrypoint) : ".");
14054
+ const entrypointAbsDir = (0, import_path21.join)(workPath, entryDirectory);
14055
+ const rootDir = repoRootPath ?? workPath;
14056
+ const venvPath = service?.name ? (0, import_path21.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path21.join)(workPath, ".vercel", "python", ".venv");
14057
+ const { pythonPackage, pythonVersion, uv, uvCacheDir, restoredCache } = await setupPythonToolchain({
14058
+ builderSpan,
14059
+ workPath,
14060
+ entrypointDir: entrypointAbsDir,
14061
+ rootDir,
14062
+ venvPath,
14063
+ meta
14064
+ });
14065
+ if ((0, import_build_utils24.isPythonFramework)(framework)) {
14066
+ const {
14067
+ cliType,
14068
+ lockfileVersion,
14069
+ packageJsonPackageManager,
14070
+ packageJsonDevEngines,
14071
+ turboSupportsCorepackHome
14072
+ } = await (0, import_build_utils24.scanParentDirs)(workPath, true);
14073
+ spawnEnv = (0, import_build_utils24.getEnvForPackageManager)({
14074
+ cliType,
14075
+ lockfileVersion,
14076
+ packageJsonPackageManager,
14077
+ packageJsonDevEngines,
14078
+ env: process.env,
14079
+ turboSupportsCorepackHome,
14080
+ projectCreatedAt: config?.projectSettings?.createdAt
14081
+ });
14082
+ const installCommand = config?.projectSettings?.installCommand;
14083
+ if (typeof installCommand === "string") {
14084
+ const trimmed = installCommand.trim();
14085
+ if (trimmed) {
14086
+ projectInstallCommand = trimmed;
14087
+ } else {
14088
+ console.log('Skipping "install" command...');
14089
+ }
14090
+ }
14091
+ }
14092
+ const baseEnv = spawnEnv || process.env;
14093
+ const pythonEnv = createVenvEnv(venvPath, baseEnv, uvCacheDir);
14094
+ pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
14095
+ let assumeDepsInstalled = false;
14096
+ let uvLockPath = null;
14097
+ let uvProjectDir = null;
14098
+ let projectName;
14099
+ let usedUvManagedInstall = false;
14100
+ await builderSpan.child(import_build_utils24.BUILDER_INSTALLER_STEP, {
14101
+ installCommand: projectInstallCommand || void 0,
14102
+ runtime: "python",
14103
+ "python.cache.restored": restoredCache
14104
+ }).trace(async () => {
14105
+ if (projectInstallCommand) {
14106
+ await import_fs20.default.promises.rm(venvPath, { recursive: true, force: true });
14107
+ await ensureVenv({
14108
+ pythonVersion,
14109
+ venvPath,
14110
+ uvPath: uv.getPath(),
14111
+ uvCacheDir,
14112
+ quiet: true
14113
+ });
14114
+ console.log(
14115
+ `Running "install" command: \`${projectInstallCommand}\`...`
14116
+ );
14117
+ await (0, import_build_utils24.execCommand)(projectInstallCommand, {
14118
+ env: pythonEnv,
14119
+ cwd: workPath
14120
+ });
14121
+ assumeDepsInstalled = true;
14122
+ hasCustomCommand = true;
14123
+ } else {
14124
+ const hasCustomScript = await runPyprojectScript(
14125
+ workPath,
14126
+ ["vercel-install", "now-install", "install"],
14127
+ pythonEnv,
14128
+ /* useUserVirtualEnv */
14129
+ false
14130
+ );
14131
+ if (hasCustomScript) {
14132
+ assumeDepsInstalled = true;
14133
+ hasCustomCommand = true;
14134
+ }
14135
+ }
14136
+ if (!assumeDepsInstalled) {
14137
+ const { projectDir, lockPath } = await runUvManagedInstall({
14138
+ workPath,
14139
+ rootDir,
14140
+ venvPath,
14141
+ pythonPackage,
14142
+ pythonVersion,
14143
+ uv,
14144
+ uvCacheDir,
14145
+ lockCacheKey: service?.name ? `uv.lock.${service.name}` : "uv.lock",
14146
+ pythonPlatform: target.uvPlatform,
14147
+ extraSyncParams: {}
14148
+ });
14149
+ uvLockPath = lockPath;
14150
+ uvProjectDir = projectDir;
14151
+ projectName = pythonPackage?.manifest?.data?.project?.name;
14152
+ usedUvManagedInstall = true;
14153
+ }
14154
+ });
14155
+ if ((0, import_build_utils24.isPythonFramework)(framework)) {
14156
+ const projectBuildCommand = config?.projectSettings?.buildCommand ?? // fallback if provided directly on config (some callers set this)
14157
+ config?.buildCommand;
14158
+ await builderSpan.child(import_build_utils24.BUILDER_COMPILE_STEP, {
14159
+ buildCommand: projectBuildCommand || void 0
14160
+ }).trace(async () => {
14161
+ if (projectBuildCommand) {
14162
+ console.log(`Running "${projectBuildCommand}"`);
14163
+ await (0, import_build_utils24.execCommand)(projectBuildCommand, {
14164
+ env: pythonEnv,
14165
+ cwd: workPath
14166
+ });
14167
+ } else {
14168
+ await runPyprojectScript(
14169
+ workPath,
14170
+ ["vercel-build", "now-build", "build"],
14171
+ pythonEnv
14172
+ );
14173
+ }
14174
+ });
14175
+ }
14176
+ const hookResult = await runFrameworkHook(framework, {
14177
+ pythonEnv,
14178
+ workPath,
14179
+ venvPath,
14180
+ entrypoint,
14181
+ detected,
14182
+ pyprojectData: pythonPackage.manifest?.data
14183
+ });
14184
+ const resolved = isPyprojectEntrypoint ? detected?.entrypoint : hookResult?.entrypoint ?? detected?.entrypoint;
14185
+ if (!resolved && detected?.error) {
14186
+ throw detected?.error;
14187
+ }
14188
+ entrypoint = resolved?.entrypoint;
14189
+ const isWorkersOnly = !entrypoint && isPyprojectEntrypoint && (subscriberDeclarations.length > 0 || workflows.length > 0);
14190
+ if (!entrypoint && !isWorkersOnly) {
14191
+ throw new import_build_utils24.NowBuildError({
14192
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
14193
+ message: "No Python entrypoint could be detected. Please specify an entrypoint file."
14194
+ });
14195
+ }
14196
+ const djangoStatic = hookResult?.djangoStatic ?? null;
14197
+ const fastapiStatic = hookResult?.fastapiStatic ?? null;
14198
+ const importSeeds = hookResult?.importSeeds ?? [];
14199
+ const cdnOutputDir = djangoStatic?.cdnOutputDir ?? fastapiStatic?.cdnOutputDir ?? null;
14200
+ const pipPlatformArgs = target.uvPlatform ? ["--python-platform", target.uvPlatform] : [];
14201
+ await installInjectedPackage({
14202
+ name: "vercel-runtime",
14203
+ requirement: `vercel-runtime==${VERCEL_RUNTIME_VERSION}`,
14204
+ envOverride: baseEnv.VERCEL_RUNTIME_PYTHON,
14205
+ allowLocalSource: true,
14206
+ uv,
14207
+ venvPath,
14208
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14209
+ pipPlatformArgs
14210
+ });
14211
+ const queueAdapterBootstrap = await getQueueAdapterBootstrap({
14212
+ pythonPackage,
14213
+ env: baseEnv,
14214
+ legacyWorkersProject,
14215
+ hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
14216
+ });
14217
+ const queueAdapterInjectedPackages = usedUvManagedInstall ? queueAdapterBootstrap.injectedPackages : [];
14218
+ for (const injectedPackage of queueAdapterInjectedPackages) {
14219
+ await installInjectedPackage({
14220
+ ...injectedPackage,
14221
+ uv,
14222
+ venvPath,
14223
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14224
+ pipPlatformArgs
14225
+ });
14226
+ }
14227
+ const localSdkSourcePaths = await getLocalSdkSourcePaths({
14228
+ pythonPackage,
14229
+ env: baseEnv
14230
+ });
14231
+ if (localSdkSourcePaths.length > 0) {
14232
+ (0, import_build_utils24.debug)(
14233
+ `Installing vercel SDK override from ${baseEnv.VERCEL_SDK_PYTHON}: ` + localSdkSourcePaths.join(", ")
14234
+ );
14235
+ await uv.pip({
14236
+ venvPath,
14237
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14238
+ args: [
14239
+ "install",
14240
+ "--link-mode",
14241
+ "copy",
14242
+ // Without --no-sources, uv resolves the checkout's workspace
14243
+ // dependencies as editable .pth installs pointing at the (build
14244
+ // only) checkout mount — invisible to bundling and dangling at
14245
+ // runtime. Explicit paths stay real installs; the remaining
14246
+ // workspace siblings resolve from PyPI.
14247
+ "--no-sources",
14248
+ ...pipPlatformArgs,
14249
+ ...localSdkSourcePaths
14250
+ ]
14251
+ });
14252
+ }
14253
+ if (workflows.length > 0) {
14254
+ workflowMode = await resolveWorkflowServingMode({
14255
+ pythonPackage,
14256
+ uv,
14257
+ venvPath,
14258
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14259
+ uvLockPath
14260
+ });
14261
+ if (workflowMode === "workers" && workflows.length > 1) {
14262
+ throw new import_build_utils24.NowBuildError({
14263
+ code: "PYTHON_INVALID_WORKFLOW_CONFIG",
14264
+ message: `"tool.vercel.workflows" declares multiple entrypoints, which requires ${QUEUE_WORKFLOW_SDK_REQUIREMENT} with a distinct namespace per Workflows registry; the installed SDK serves every workflow on the shared __wkf_* topic`
14265
+ });
14266
+ }
14267
+ }
14268
+ const shouldInstallVercelWorkers = legacyWorkersProject || workflows.length > 0 && workflowMode === "workers";
14269
+ if (shouldInstallVercelWorkers) {
14270
+ await installInjectedPackage({
14271
+ name: "vercel-workers",
14272
+ requirement: `vercel-workers==${VERCEL_WORKERS_VERSION}`,
14273
+ envOverride: baseEnv.VERCEL_WORKERS_PYTHON,
14274
+ allowLocalSource: true,
14275
+ uv,
14276
+ venvPath,
14277
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14278
+ pipPlatformArgs
14279
+ });
14280
+ }
14281
+ const quirksResult = await runQuirks({ venvPath, pythonEnv, workPath });
14282
+ if (quirksResult.buildEnv) {
14283
+ Object.assign(pythonEnv, quirksResult.buildEnv);
14284
+ }
14285
+ const queueIntegrations = queueAdapterBootstrap.integrations;
14286
+ const writeGeneratedQueueHandler = async (outputPath, declaration) => {
14287
+ const generatedPath = getGeneratedQueueHandlerPath(outputPath);
14288
+ await import_fs20.default.promises.mkdir((0, import_path21.dirname)((0, import_path21.join)(workPath, generatedPath)), {
14289
+ recursive: true
14290
+ });
14291
+ await import_fs20.default.promises.writeFile(
14292
+ (0, import_path21.join)(workPath, generatedPath),
14293
+ createQueueHandlerModule(declaration, queueIntegrations)
14294
+ );
14295
+ };
14296
+ if (subscriberDeclarations.length > 0 && !legacyWorkersProject) {
14297
+ subscribers = await resolveQueueSubscribers({
14298
+ declarations: subscriberDeclarations,
14299
+ uv,
14300
+ venvPath,
14301
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14302
+ integrations: queueIntegrations
14303
+ });
14304
+ if (workflowMode === "queue") {
14305
+ for (const subscriber of subscribers) {
14306
+ if (!subscriber.topicPatterns) {
14307
+ subscriber.subscriptions = subscriber.subscriptions.filter(
14308
+ (subscription) => !isWorkflowQueueTopic(subscription.topic)
14309
+ );
14310
+ }
14311
+ }
14312
+ }
14313
+ for (const subscriber of subscribers) {
14314
+ await writeGeneratedQueueHandler(
14315
+ getSubscriberOutputPath(subscriber.name),
14316
+ subscriber
14317
+ );
14318
+ }
14319
+ }
14320
+ const workflowQueueSubscriptions = /* @__PURE__ */ new Map();
14321
+ if (workflows.length > 0 && workflowMode === "queue") {
14322
+ const resolved2 = await resolveQueueSubscribers({
14323
+ declarations: workflows.map((workflow) => ({
14324
+ name: workflow.name,
14325
+ entrypoint: workflow.entrypoint,
14326
+ moduleName: workflow.moduleName,
14327
+ variableName: workflow.variableName
14328
+ })),
14329
+ uv,
14330
+ venvPath,
14331
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14332
+ kind: "workflow",
14333
+ integrations: queueIntegrations
14334
+ });
14335
+ for (let i = 0; i < resolved2.length; i++) {
14336
+ for (let j = i + 1; j < resolved2.length; j++) {
14337
+ const overlaps = resolved2[i].subscriptions.some(
14338
+ (left) => resolved2[j].subscriptions.some(
14339
+ (right) => queueTopicPatternsOverlap(left.topic, right.topic)
14340
+ )
14341
+ );
14342
+ if (overlaps) {
14343
+ throw new import_build_utils24.NowBuildError({
14344
+ code: "PYTHON_INVALID_WORKFLOW_CONFIG",
14345
+ message: `workflow entrypoints "${resolved2[i].moduleName}:${resolved2[i].variableName}" and "${resolved2[j].moduleName}:${resolved2[j].variableName}" subscribe to overlapping queue topics; construct each vercel.workflow.Workflows registry with a distinct namespace`
14346
+ });
14347
+ }
14348
+ }
14349
+ }
14350
+ for (const workflow of resolved2) {
14351
+ workflowQueueSubscriptions.set(workflow.name, workflow.subscriptions);
14352
+ await writeGeneratedQueueHandler(
14353
+ getWorkflowOutputPath(workflow.name),
14354
+ workflow
14355
+ );
14356
+ }
14357
+ }
14358
+ const preDeployCommand = config?.preDeployCommand;
14359
+ if (registerPreDeploy && typeof preDeployCommand === "string") {
14360
+ const capturedEnv = { ...pythonEnv };
14361
+ const capturedCwd = workPath;
14362
+ registerPreDeploy(async () => {
14363
+ await builderSpan.child(import_build_utils24.BUILDER_PRE_DEPLOY_STEP, {
14364
+ preDeployCommand
14365
+ }).trace(async () => {
14366
+ console.log(`Running pre-deploy command: \`${preDeployCommand}\``);
14367
+ await (0, import_build_utils24.execCommand)(preDeployCommand, {
14368
+ env: capturedEnv,
14369
+ cwd: capturedCwd
14370
+ });
14371
+ });
14372
+ });
14373
+ }
14374
+ const vendorDir = resolveVendorDir();
14375
+ let crons;
14376
+ let runtimeTrampoline;
14377
+ if (entrypoint) {
14378
+ (0, import_build_utils24.debug)("Entrypoint is", entrypoint);
14379
+ const moduleName = entrypointToModule(entrypoint);
14380
+ if (handlerFunction) {
14381
+ const entrypointPath = (0, import_path21.join)(workPath, entrypoint);
14382
+ const source = await import_fs20.default.promises.readFile(entrypointPath, "utf-8");
14383
+ const found = await (0, import_python_analysis12.containsTopLevelCallable)(source, handlerFunction);
14384
+ if (!found) {
14385
+ throw new import_build_utils24.NowBuildError({
14386
+ code: "PYTHON_HANDLER_NOT_FOUND",
14387
+ message: `Handler function "${handlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
14388
+ });
14389
+ }
14390
+ }
14391
+ const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
14392
+ const entrypointWithSuffix = `${entrypoint}${suffix}`;
14393
+ (0, import_build_utils24.debug)("Entrypoint with suffix is", entrypointWithSuffix);
14394
+ crons = await getServiceCrons({
14395
+ service,
14396
+ entrypoint,
14397
+ rawEntrypoint,
14398
+ handlerFunction,
14399
+ pythonBin: getVenvPythonBin(venvPath),
14400
+ env: pythonEnv,
14401
+ workPath
14402
+ });
14403
+ const extraTrampolineEnv = [];
14404
+ if (crons?.length) {
14405
+ const json = JSON.stringify(buildCronRouteTable(crons));
14406
+ (0, import_assert.default)(!json.includes("\\"), `backslash in cron route table: ${json}`);
14407
+ extraTrampolineEnv.push(`"__VC_CRON_ROUTES": '${json}'`);
14408
+ }
14409
+ const variableName = resolved?.variableName ?? "";
14410
+ runtimeTrampoline = createRuntimeTrampoline({
14411
+ moduleName,
14412
+ entrypoint: entrypointWithSuffix,
14413
+ vendorDir,
14414
+ variableName,
14415
+ extraEnv: extraTrampolineEnv
14416
+ });
14417
+ }
14418
+ const compileAllEnabled = shouldCompileAll({
14419
+ isDev: meta.isDev,
14420
+ hasCustomCommand,
14421
+ // A pre-deploy command can rewrite source after the build, which would make
14422
+ // unchecked-hash precompiled bytecode stale; skip precompilation to avoid serving it.
14423
+ hasPreDeployCommand: typeof preDeployCommand === "string"
14424
+ });
14425
+ const lambdaEnv = {};
14426
+ lambdaEnv.PYTHONPATH = vendorDir;
14427
+ lambdaEnv.PYTHONDONTWRITEBYTECODE = "1";
14428
+ Object.assign(lambdaEnv, quirksResult.env);
14429
+ if (shouldInstallVercelWorkers) {
14430
+ lambdaEnv.VERCEL_HAS_WORKER_SERVICES = "1";
14431
+ }
14432
+ if (queueIntegrations.length > 0) {
14433
+ lambdaEnv.VERCEL_QUEUE_INTEGRATIONS = queueIntegrations.map(
14434
+ ({ module: module2, installer, servingActivator }) => `${module2}:${installer}` + (servingActivator ? `:${servingActivator}` : "")
14435
+ ).join(",");
14436
+ }
14437
+ const apschedulerSubscribers = apschedulerSubscriberIdentities(subscribers);
14438
+ if (apschedulerSubscribers.length > 0) {
14439
+ lambdaEnv.VERCEL_APSCHEDULER_SUBSCRIBERS = JSON.stringify(
14440
+ apschedulerSubscribers
14441
+ );
14442
+ const previewConfig = await getApschedulerPreviewConfig(workPath);
14443
+ if (previewConfig) {
14444
+ lambdaEnv.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS = String(
14445
+ previewConfig.idleTimeoutSeconds
14446
+ );
14447
+ }
14448
+ }
14449
+ const functionConfig = getMatchingFunction(
14450
+ config,
14451
+ entrypoint
14452
+ )?.functionConfig;
14453
+ const includeFilesPatterns = [
14454
+ ...normalizeGlobs(config?.includeFiles),
14455
+ ...normalizeGlobs(functionConfig?.includeFiles)
14456
+ ];
14457
+ const excludeFilesPatterns = [
14458
+ ...normalizeGlobs(config?.excludeFiles),
14459
+ ...normalizeGlobs(functionConfig?.excludeFiles)
14460
+ ];
14461
+ const cdnBundle = getFastAPICdnBundle(
14462
+ fastapiStatic?.collectedMounts ?? [],
14463
+ workPath,
14464
+ pythonPackage.manifest?.data?.tool?.vercel?.fastapi
14465
+ );
14466
+ const initialFiles = {};
14467
+ for (const pattern of includeFilesPatterns) {
14468
+ Object.assign(initialFiles, await (0, import_build_utils24.glob)(pattern, workPath));
14469
+ }
14470
+ Object.assign(initialFiles, cdnBundle.directoryStubs);
14471
+ if (djangoStatic?.manifestRelPath) {
14472
+ initialFiles[djangoStatic.manifestRelPath] = new import_build_utils24.FileFsRef({
14473
+ fsPath: (0, import_path21.join)(workPath, djangoStatic.manifestRelPath)
14474
+ });
14475
+ }
14476
+ const handlerPyFilename = "vc__handler__python";
14477
+ if (config.framework === "fasthtml") {
14478
+ const { SESSKEY = "" } = process.env;
14479
+ initialFiles[".sesskey"] = new import_build_utils24.FileBlob({ data: `"${SESSKEY}"` });
14480
+ }
14481
+ const { pycachePrefix, files } = await packFunctionBundle({
14482
+ builderSpan,
14483
+ workPath,
14484
+ uvLockPath,
14485
+ uvProjectDir,
14486
+ projectName,
14487
+ pythonVersion,
14488
+ hasCustomCommand,
14489
+ alwaysBundlePackages: [
14490
+ ...quirksResult.alwaysBundlePackages ?? [],
14491
+ ...shouldInstallVercelWorkers ? ["vercel-workers", "vercel_workers"] : []
14492
+ ],
14493
+ initialFiles,
14494
+ additionalExcludes: [...excludeFilesPatterns, ...cdnBundle.excludePatterns],
14495
+ compileAllEnabled,
14496
+ compileEnv: pythonEnv,
14497
+ venvPath,
14498
+ entrypoint,
14499
+ importClosureContext: {
14500
+ frameworkSeeds: importSeeds,
14501
+ extraPythonPath: hookResult?.extraPythonPath,
14502
+ subscriberDeclarations,
14503
+ subscribers,
14504
+ workflows,
14505
+ workflowMode
14506
+ },
14507
+ largeFunctionLabel: entrypoint ?? rawEntrypoint
14508
+ });
14509
+ if (pycachePrefix) {
14510
+ lambdaEnv.PYTHONPYCACHEPREFIX = pycachePrefix;
14511
+ }
14238
14512
  let output;
14239
14513
  if (entrypoint && runtimeTrampoline) {
14240
14514
  const webFiles = {