@vercel/python 14.2.0 → 15.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 +627 -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
  }
@@ -13330,95 +13334,17 @@ async function installInjectedPackage({
13330
13334
  ]
13331
13335
  });
13332
13336
  }
13333
- var build = async ({
13337
+ async function setupPythonToolchain({
13338
+ builderSpan,
13334
13339
  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;
13340
+ entrypointDir,
13341
+ rootDir,
13342
+ venvPath,
13343
+ meta
13344
+ }) {
13419
13345
  const pythonPackage = await builderSpan.child("vc.builder.python.discover").trace(
13420
13346
  () => discoverPackage({
13421
- entrypointDir: entrypointAbsDir,
13347
+ entrypointDir,
13422
13348
  rootDir
13423
13349
  })
13424
13350
  );
@@ -13455,7 +13381,6 @@ var build = async ({
13455
13381
  }
13456
13382
  const uvVersion = checkUvBinaryVersion(uv.getPath());
13457
13383
  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
13384
  const hasCachedVenv = import_fs20.default.existsSync((0, import_path21.join)(venvPath, "pyvenv.cfg"));
13460
13385
  const hasCachedUv = import_fs20.default.existsSync(uvCacheDir);
13461
13386
  const restoredCache = hasCachedVenv && hasCachedUv ? "both" : hasCachedVenv ? "venv" : hasCachedUv ? "uv" : "none";
@@ -13472,377 +13397,67 @@ var build = async ({
13472
13397
  uvCacheDir
13473
13398
  });
13474
13399
  });
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,
13400
+ return { pythonPackage, pythonVersion, uv, uvCacheDir, restoredCache };
13401
+ }
13402
+ async function runUvManagedInstall({
13403
+ workPath,
13404
+ rootDir,
13405
+ venvPath,
13406
+ pythonPackage,
13407
+ pythonVersion,
13408
+ uv,
13409
+ uvCacheDir,
13410
+ lockCacheKey,
13411
+ pythonPlatform,
13412
+ extraSyncParams
13413
+ }) {
13414
+ const cachedLockPath = (0, import_path21.join)(uvCacheDir, lockCacheKey);
13415
+ const result = await ensureUvProject({
13599
13416
  workPath,
13417
+ rootDir,
13600
13418
  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
13419
  pythonPackage,
13634
- env: baseEnv,
13635
- legacyWorkersProject,
13636
- hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
13420
+ pythonVersion: pythonVersionString(pythonVersion),
13421
+ uv,
13422
+ requireBinaryWheels: false,
13423
+ cachedLockPath
13637
13424
  });
13638
- const queueAdapterInjectedPackages = usedUvManagedInstall ? queueAdapterBootstrap.injectedPackages : [];
13639
- for (const injectedPackage of queueAdapterInjectedPackages) {
13640
- await installInjectedPackage({
13641
- ...injectedPackage,
13642
- uv,
13425
+ const { projectDir, lockPath, lockFileProvidedByUser } = result;
13426
+ if (extraSyncParams !== void 0) {
13427
+ await uv.sync({
13643
13428
  venvPath,
13644
- projectDir: (0, import_path21.join)(workPath, entryDirectory),
13645
- pipPlatformArgs
13429
+ projectDir,
13430
+ frozen: lockFileProvidedByUser,
13431
+ locked: !lockFileProvidedByUser,
13432
+ pythonPlatform,
13433
+ ...extraSyncParams
13646
13434
  });
13647
13435
  }
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
- });
13436
+ if (lockPath && import_fs20.default.existsSync(lockPath)) {
13437
+ await import_fs20.default.promises.mkdir(uvCacheDir, { recursive: true });
13438
+ await import_fs20.default.promises.copyFile(lockPath, cachedLockPath);
13794
13439
  }
13440
+ return result;
13441
+ }
13442
+ async function packFunctionBundle({
13443
+ builderSpan,
13444
+ workPath,
13445
+ uvLockPath,
13446
+ uvProjectDir,
13447
+ projectName,
13448
+ pythonVersion,
13449
+ hasCustomCommand,
13450
+ alwaysBundlePackages,
13451
+ initialFiles,
13452
+ additionalExcludes,
13453
+ compileAllEnabled,
13454
+ compileEnv,
13455
+ venvPath,
13456
+ entrypoint,
13457
+ importClosureContext,
13458
+ largeFunctionLabel
13459
+ }) {
13795
13460
  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
13461
  const predefinedExcludes = [
13847
13462
  ".git/**",
13848
13463
  ".gitignore",
@@ -13861,71 +13476,13 @@ var build = async ({
13861
13476
  "**/yarn.lock",
13862
13477
  "**/package-lock.json"
13863
13478
  ];
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 = {
13479
+ const files = await (0, import_build_utils24.glob)("**", {
13906
13480
  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);
13481
+ ignore: [...predefinedExcludes, ...additionalExcludes ?? []]
13482
+ });
13483
+ Object.assign(files, initialFiles);
13918
13484
  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
- }
13485
+ let pycachePrefix;
13929
13486
  await builderSpan.child("vc.builder.python.bundle").trace(async (bundleSpan) => {
13930
13487
  const installedDistributions = await InstalledPythonDistributions.load({
13931
13488
  venvPath,
@@ -13941,10 +13498,7 @@ var build = async ({
13941
13498
  projectName,
13942
13499
  pythonVersion,
13943
13500
  hasCustomCommand,
13944
- alwaysBundlePackages: [
13945
- ...quirksResult.alwaysBundlePackages ?? [],
13946
- ...shouldInstallVercelWorkers ? ["vercel-workers", "vercel_workers"] : []
13947
- ]
13501
+ alwaysBundlePackages
13948
13502
  });
13949
13503
  const depAnalysis = await depExternalizer.analyze(files, {
13950
13504
  onSized: ({ totalSizeBytes, runtimeInstallEnabled }) => {
@@ -13958,11 +13512,11 @@ var build = async ({
13958
13512
  });
13959
13513
  const compileAllOptions = compileAllEnabled ? {
13960
13514
  pythonBin: getVenvPythonBin(venvPath),
13961
- env: pythonEnv
13515
+ env: compileEnv
13962
13516
  } : null;
13963
13517
  const compileSources = async ({
13964
13518
  includePackages,
13965
- pycachePrefix
13519
+ pycachePrefix: pycachePrefixArg
13966
13520
  }) => {
13967
13521
  if (!compileAllOptions)
13968
13522
  return void 0;
@@ -13973,7 +13527,7 @@ var build = async ({
13973
13527
  const result = await runCompileAll({
13974
13528
  ...compileAllOptions,
13975
13529
  sourceFiles: [...appPythonSourceFiles, ...vendorSourceFiles],
13976
- pycachePrefix
13530
+ pycachePrefix: pycachePrefixArg
13977
13531
  });
13978
13532
  timings = result.timings;
13979
13533
  compileSpan.setAttributes({
@@ -13998,13 +13552,8 @@ var build = async ({
13998
13552
  getImportClosureOptions({
13999
13553
  workPath,
14000
13554
  entrypoint,
14001
- frameworkSeeds: importSeeds,
14002
- extraPythonPath: hookResult?.extraPythonPath,
14003
- subscriberDeclarations,
14004
- subscribers,
14005
- workflows,
14006
- workflowMode,
14007
- sitePackageDirs
13555
+ sitePackageDirs,
13556
+ ...importClosureContext
14008
13557
  })
14009
13558
  ),
14010
13559
  IMPORT_CLOSURE_TIMEOUT_MS,
@@ -14158,7 +13707,7 @@ var build = async ({
14158
13707
  timings
14159
13708
  });
14160
13709
  if (bytesAdded > 0) {
14161
- lambdaEnv.PYTHONPYCACHEPREFIX = RUNTIME_PYCACHE_PREFIX;
13710
+ pycachePrefix = RUNTIME_PYCACHE_PREFIX;
14162
13711
  }
14163
13712
  } catch (err) {
14164
13713
  console.log(
@@ -14168,7 +13717,7 @@ var build = async ({
14168
13717
  }
14169
13718
  };
14170
13719
  const announceLargeFunction = () => console.log(
14171
- `Function "${entrypoint ?? rawEntrypoint}" exceeds the standard size limit; enabling large functions (beta).`
13720
+ `Function "${largeFunctionLabel}" exceeds the standard size limit; enabling large functions (beta).`
14172
13721
  );
14173
13722
  let packingMode;
14174
13723
  let runtimeToolingBytes = 0;
@@ -14235,6 +13784,556 @@ var build = async ({
14235
13784
  "python.bundle.packingMode": packingMode
14236
13785
  });
14237
13786
  });
13787
+ return { pycachePrefix, vendorDir, files };
13788
+ }
13789
+ var build = async ({
13790
+ workPath,
13791
+ repoRootPath,
13792
+ files: originalFiles,
13793
+ entrypoint: rawEntrypoint,
13794
+ meta = {},
13795
+ config,
13796
+ span: parentSpan,
13797
+ service,
13798
+ registerPreDeploy
13799
+ }) => {
13800
+ if (config?.middleware === true) {
13801
+ throw new import_build_utils24.NowBuildError({
13802
+ code: "PYTHON_PROXY_NOT_IMPLEMENTED",
13803
+ message: "Python proxy middleware is not yet supported."
13804
+ });
13805
+ }
13806
+ let entrypoint = rawEntrypoint === "<detect>" ? void 0 : rawEntrypoint;
13807
+ const builderSpan = parentSpan ?? new import_build_utils24.Span({ name: "vc.builder" });
13808
+ const isPyprojectEntrypoint = entrypoint !== void 0 && (0, import_path21.basename)(entrypoint) === "pyproject.toml";
13809
+ if (isPyprojectEntrypoint && entrypoint !== "pyproject.toml") {
13810
+ throw new import_build_utils24.NowBuildError({
13811
+ code: "PYTHON_INVALID_PYPROJECT_ENTRYPOINT",
13812
+ 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".`
13813
+ });
13814
+ }
13815
+ const framework = config?.framework;
13816
+ let subscriberDeclarations = [];
13817
+ let subscribers = [];
13818
+ let workflows = [];
13819
+ let legacyWorkersProject = false;
13820
+ let workflowMode = "workers";
13821
+ let spawnEnv;
13822
+ let projectInstallCommand;
13823
+ let hasCustomCommand = false;
13824
+ const target = getTargetPlatform(meta.isDev ?? false);
13825
+ (0, import_build_utils24.debug)(`workPath: ${workPath}`);
13826
+ workPath = await downloadFilesInWorkPath({
13827
+ workPath,
13828
+ files: originalFiles,
13829
+ entrypoint,
13830
+ meta
13831
+ });
13832
+ legacyWorkersProject = await isLegacyWorkersProject(workPath);
13833
+ if (isPyprojectEntrypoint || !service && (0, import_build_utils24.isPythonFramework)(framework)) {
13834
+ subscriberDeclarations = await getPyprojectSubscribers(workPath, {
13835
+ legacySchema: legacyWorkersProject
13836
+ });
13837
+ workflows = await getPyprojectWorkflows(workPath);
13838
+ }
13839
+ try {
13840
+ if (meta.isDev) {
13841
+ const setupCfg = (0, import_path21.join)(workPath, "setup.cfg");
13842
+ await writeFile(setupCfg, "[install]\nprefix=\n");
13843
+ }
13844
+ } catch (err) {
13845
+ console.log('Failed to create "setup.cfg" file');
13846
+ throw err;
13847
+ }
13848
+ let detected;
13849
+ const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
13850
+ if (isPyprojectEntrypoint) {
13851
+ const declared = await getVercelToolsEntrypoint(workPath, repoRootPath);
13852
+ if (declared) {
13853
+ detected = { entrypoint: declared };
13854
+ } else if (subscriberDeclarations.length === 0 && workflows.length === 0) {
13855
+ throw new import_build_utils24.NowBuildError({
13856
+ code: "PYTHON_PYPROJECT_NOTHING_TO_BUILD",
13857
+ 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.'
13858
+ });
13859
+ }
13860
+ entrypoint = void 0;
13861
+ } else {
13862
+ detected = await detectPythonEntrypoint(
13863
+ config.framework,
13864
+ workPath,
13865
+ entrypoint ? {
13866
+ filePath: entrypoint,
13867
+ // For schedule-triggered jobs, the WSGI variable is always 'app' (created dynamically).
13868
+ // For other services, handlerFunction is used as the entrypoint variable name.
13869
+ varName: service && (0, import_build_utils24.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
13870
+ } : void 0,
13871
+ service,
13872
+ repoRootPath
13873
+ ) ?? void 0;
13874
+ }
13875
+ if (detected?.error && detected?.baseDir === void 0) {
13876
+ throw detected?.error;
13877
+ }
13878
+ const entryDirectory = detected?.baseDir ?? (entrypoint ? (0, import_path21.dirname)(entrypoint) : ".");
13879
+ const entrypointAbsDir = (0, import_path21.join)(workPath, entryDirectory);
13880
+ const rootDir = repoRootPath ?? workPath;
13881
+ const venvPath = service?.name ? (0, import_path21.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path21.join)(workPath, ".vercel", "python", ".venv");
13882
+ const { pythonPackage, pythonVersion, uv, uvCacheDir, restoredCache } = await setupPythonToolchain({
13883
+ builderSpan,
13884
+ workPath,
13885
+ entrypointDir: entrypointAbsDir,
13886
+ rootDir,
13887
+ venvPath,
13888
+ meta
13889
+ });
13890
+ if ((0, import_build_utils24.isPythonFramework)(framework)) {
13891
+ const {
13892
+ cliType,
13893
+ lockfileVersion,
13894
+ packageJsonPackageManager,
13895
+ packageJsonDevEngines,
13896
+ turboSupportsCorepackHome
13897
+ } = await (0, import_build_utils24.scanParentDirs)(workPath, true);
13898
+ spawnEnv = (0, import_build_utils24.getEnvForPackageManager)({
13899
+ cliType,
13900
+ lockfileVersion,
13901
+ packageJsonPackageManager,
13902
+ packageJsonDevEngines,
13903
+ env: process.env,
13904
+ turboSupportsCorepackHome,
13905
+ projectCreatedAt: config?.projectSettings?.createdAt
13906
+ });
13907
+ const installCommand = config?.projectSettings?.installCommand;
13908
+ if (typeof installCommand === "string") {
13909
+ const trimmed = installCommand.trim();
13910
+ if (trimmed) {
13911
+ projectInstallCommand = trimmed;
13912
+ } else {
13913
+ console.log('Skipping "install" command...');
13914
+ }
13915
+ }
13916
+ }
13917
+ const baseEnv = spawnEnv || process.env;
13918
+ const pythonEnv = createVenvEnv(venvPath, baseEnv, uvCacheDir);
13919
+ pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
13920
+ let assumeDepsInstalled = false;
13921
+ let uvLockPath = null;
13922
+ let uvProjectDir = null;
13923
+ let projectName;
13924
+ let usedUvManagedInstall = false;
13925
+ await builderSpan.child(import_build_utils24.BUILDER_INSTALLER_STEP, {
13926
+ installCommand: projectInstallCommand || void 0,
13927
+ runtime: "python",
13928
+ "python.cache.restored": restoredCache
13929
+ }).trace(async () => {
13930
+ if (projectInstallCommand) {
13931
+ await import_fs20.default.promises.rm(venvPath, { recursive: true, force: true });
13932
+ await ensureVenv({
13933
+ pythonVersion,
13934
+ venvPath,
13935
+ uvPath: uv.getPath(),
13936
+ uvCacheDir,
13937
+ quiet: true
13938
+ });
13939
+ console.log(
13940
+ `Running "install" command: \`${projectInstallCommand}\`...`
13941
+ );
13942
+ await (0, import_build_utils24.execCommand)(projectInstallCommand, {
13943
+ env: pythonEnv,
13944
+ cwd: workPath
13945
+ });
13946
+ assumeDepsInstalled = true;
13947
+ hasCustomCommand = true;
13948
+ } else {
13949
+ const hasCustomScript = await runPyprojectScript(
13950
+ workPath,
13951
+ ["vercel-install", "now-install", "install"],
13952
+ pythonEnv,
13953
+ /* useUserVirtualEnv */
13954
+ false
13955
+ );
13956
+ if (hasCustomScript) {
13957
+ assumeDepsInstalled = true;
13958
+ hasCustomCommand = true;
13959
+ }
13960
+ }
13961
+ if (!assumeDepsInstalled) {
13962
+ const { projectDir, lockPath } = await runUvManagedInstall({
13963
+ workPath,
13964
+ rootDir,
13965
+ venvPath,
13966
+ pythonPackage,
13967
+ pythonVersion,
13968
+ uv,
13969
+ uvCacheDir,
13970
+ lockCacheKey: service?.name ? `uv.lock.${service.name}` : "uv.lock",
13971
+ pythonPlatform: target.uvPlatform,
13972
+ extraSyncParams: {}
13973
+ });
13974
+ uvLockPath = lockPath;
13975
+ uvProjectDir = projectDir;
13976
+ projectName = pythonPackage?.manifest?.data?.project?.name;
13977
+ usedUvManagedInstall = true;
13978
+ }
13979
+ });
13980
+ if ((0, import_build_utils24.isPythonFramework)(framework)) {
13981
+ const projectBuildCommand = config?.projectSettings?.buildCommand ?? // fallback if provided directly on config (some callers set this)
13982
+ config?.buildCommand;
13983
+ await builderSpan.child(import_build_utils24.BUILDER_COMPILE_STEP, {
13984
+ buildCommand: projectBuildCommand || void 0
13985
+ }).trace(async () => {
13986
+ if (projectBuildCommand) {
13987
+ console.log(`Running "${projectBuildCommand}"`);
13988
+ await (0, import_build_utils24.execCommand)(projectBuildCommand, {
13989
+ env: pythonEnv,
13990
+ cwd: workPath
13991
+ });
13992
+ } else {
13993
+ await runPyprojectScript(
13994
+ workPath,
13995
+ ["vercel-build", "now-build", "build"],
13996
+ pythonEnv
13997
+ );
13998
+ }
13999
+ });
14000
+ }
14001
+ const hookResult = await runFrameworkHook(framework, {
14002
+ pythonEnv,
14003
+ workPath,
14004
+ venvPath,
14005
+ entrypoint,
14006
+ detected,
14007
+ pyprojectData: pythonPackage.manifest?.data
14008
+ });
14009
+ const resolved = isPyprojectEntrypoint ? detected?.entrypoint : hookResult?.entrypoint ?? detected?.entrypoint;
14010
+ if (!resolved && detected?.error) {
14011
+ throw detected?.error;
14012
+ }
14013
+ entrypoint = resolved?.entrypoint;
14014
+ const isWorkersOnly = !entrypoint && isPyprojectEntrypoint && (subscriberDeclarations.length > 0 || workflows.length > 0);
14015
+ if (!entrypoint && !isWorkersOnly) {
14016
+ throw new import_build_utils24.NowBuildError({
14017
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
14018
+ message: "No Python entrypoint could be detected. Please specify an entrypoint file."
14019
+ });
14020
+ }
14021
+ const djangoStatic = hookResult?.djangoStatic ?? null;
14022
+ const fastapiStatic = hookResult?.fastapiStatic ?? null;
14023
+ const importSeeds = hookResult?.importSeeds ?? [];
14024
+ const cdnOutputDir = djangoStatic?.cdnOutputDir ?? fastapiStatic?.cdnOutputDir ?? null;
14025
+ const pipPlatformArgs = target.uvPlatform ? ["--python-platform", target.uvPlatform] : [];
14026
+ await installInjectedPackage({
14027
+ name: "vercel-runtime",
14028
+ requirement: `vercel-runtime==${VERCEL_RUNTIME_VERSION}`,
14029
+ envOverride: baseEnv.VERCEL_RUNTIME_PYTHON,
14030
+ allowLocalSource: true,
14031
+ uv,
14032
+ venvPath,
14033
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14034
+ pipPlatformArgs
14035
+ });
14036
+ const queueAdapterBootstrap = await getQueueAdapterBootstrap({
14037
+ pythonPackage,
14038
+ env: baseEnv,
14039
+ legacyWorkersProject,
14040
+ hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
14041
+ });
14042
+ const queueAdapterInjectedPackages = usedUvManagedInstall ? queueAdapterBootstrap.injectedPackages : [];
14043
+ for (const injectedPackage of queueAdapterInjectedPackages) {
14044
+ await installInjectedPackage({
14045
+ ...injectedPackage,
14046
+ uv,
14047
+ venvPath,
14048
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14049
+ pipPlatformArgs
14050
+ });
14051
+ }
14052
+ const localSdkSourcePaths = await getLocalSdkSourcePaths({
14053
+ pythonPackage,
14054
+ env: baseEnv
14055
+ });
14056
+ if (localSdkSourcePaths.length > 0) {
14057
+ (0, import_build_utils24.debug)(
14058
+ `Installing vercel SDK override from ${baseEnv.VERCEL_SDK_PYTHON}: ` + localSdkSourcePaths.join(", ")
14059
+ );
14060
+ await uv.pip({
14061
+ venvPath,
14062
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14063
+ args: [
14064
+ "install",
14065
+ "--link-mode",
14066
+ "copy",
14067
+ // Without --no-sources, uv resolves the checkout's workspace
14068
+ // dependencies as editable .pth installs pointing at the (build
14069
+ // only) checkout mount — invisible to bundling and dangling at
14070
+ // runtime. Explicit paths stay real installs; the remaining
14071
+ // workspace siblings resolve from PyPI.
14072
+ "--no-sources",
14073
+ ...pipPlatformArgs,
14074
+ ...localSdkSourcePaths
14075
+ ]
14076
+ });
14077
+ }
14078
+ if (workflows.length > 0) {
14079
+ workflowMode = await resolveWorkflowServingMode({
14080
+ pythonPackage,
14081
+ uv,
14082
+ venvPath,
14083
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14084
+ uvLockPath
14085
+ });
14086
+ if (workflowMode === "workers" && workflows.length > 1) {
14087
+ throw new import_build_utils24.NowBuildError({
14088
+ code: "PYTHON_INVALID_WORKFLOW_CONFIG",
14089
+ 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`
14090
+ });
14091
+ }
14092
+ }
14093
+ const shouldInstallVercelWorkers = legacyWorkersProject || workflows.length > 0 && workflowMode === "workers";
14094
+ if (shouldInstallVercelWorkers) {
14095
+ await installInjectedPackage({
14096
+ name: "vercel-workers",
14097
+ requirement: `vercel-workers==${VERCEL_WORKERS_VERSION}`,
14098
+ envOverride: baseEnv.VERCEL_WORKERS_PYTHON,
14099
+ allowLocalSource: true,
14100
+ uv,
14101
+ venvPath,
14102
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14103
+ pipPlatformArgs
14104
+ });
14105
+ }
14106
+ const quirksResult = await runQuirks({ venvPath, pythonEnv, workPath });
14107
+ if (quirksResult.buildEnv) {
14108
+ Object.assign(pythonEnv, quirksResult.buildEnv);
14109
+ }
14110
+ const queueIntegrations = queueAdapterBootstrap.integrations;
14111
+ const writeGeneratedQueueHandler = async (outputPath, declaration) => {
14112
+ const generatedPath = getGeneratedQueueHandlerPath(outputPath);
14113
+ await import_fs20.default.promises.mkdir((0, import_path21.dirname)((0, import_path21.join)(workPath, generatedPath)), {
14114
+ recursive: true
14115
+ });
14116
+ await import_fs20.default.promises.writeFile(
14117
+ (0, import_path21.join)(workPath, generatedPath),
14118
+ createQueueHandlerModule(declaration, queueIntegrations)
14119
+ );
14120
+ };
14121
+ if (subscriberDeclarations.length > 0 && !legacyWorkersProject) {
14122
+ subscribers = await resolveQueueSubscribers({
14123
+ declarations: subscriberDeclarations,
14124
+ uv,
14125
+ venvPath,
14126
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14127
+ integrations: queueIntegrations
14128
+ });
14129
+ if (workflowMode === "queue") {
14130
+ for (const subscriber of subscribers) {
14131
+ if (!subscriber.topicPatterns) {
14132
+ subscriber.subscriptions = subscriber.subscriptions.filter(
14133
+ (subscription) => !isWorkflowQueueTopic(subscription.topic)
14134
+ );
14135
+ }
14136
+ }
14137
+ }
14138
+ for (const subscriber of subscribers) {
14139
+ await writeGeneratedQueueHandler(
14140
+ getSubscriberOutputPath(subscriber.name),
14141
+ subscriber
14142
+ );
14143
+ }
14144
+ }
14145
+ const workflowQueueSubscriptions = /* @__PURE__ */ new Map();
14146
+ if (workflows.length > 0 && workflowMode === "queue") {
14147
+ const resolved2 = await resolveQueueSubscribers({
14148
+ declarations: workflows.map((workflow) => ({
14149
+ name: workflow.name,
14150
+ entrypoint: workflow.entrypoint,
14151
+ moduleName: workflow.moduleName,
14152
+ variableName: workflow.variableName
14153
+ })),
14154
+ uv,
14155
+ venvPath,
14156
+ projectDir: (0, import_path21.join)(workPath, entryDirectory),
14157
+ kind: "workflow",
14158
+ integrations: queueIntegrations
14159
+ });
14160
+ for (let i = 0; i < resolved2.length; i++) {
14161
+ for (let j = i + 1; j < resolved2.length; j++) {
14162
+ const overlaps = resolved2[i].subscriptions.some(
14163
+ (left) => resolved2[j].subscriptions.some(
14164
+ (right) => queueTopicPatternsOverlap(left.topic, right.topic)
14165
+ )
14166
+ );
14167
+ if (overlaps) {
14168
+ throw new import_build_utils24.NowBuildError({
14169
+ code: "PYTHON_INVALID_WORKFLOW_CONFIG",
14170
+ 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`
14171
+ });
14172
+ }
14173
+ }
14174
+ }
14175
+ for (const workflow of resolved2) {
14176
+ workflowQueueSubscriptions.set(workflow.name, workflow.subscriptions);
14177
+ await writeGeneratedQueueHandler(
14178
+ getWorkflowOutputPath(workflow.name),
14179
+ workflow
14180
+ );
14181
+ }
14182
+ }
14183
+ const preDeployCommand = config?.preDeployCommand;
14184
+ if (registerPreDeploy && typeof preDeployCommand === "string") {
14185
+ const capturedEnv = { ...pythonEnv };
14186
+ const capturedCwd = workPath;
14187
+ registerPreDeploy(async () => {
14188
+ await builderSpan.child(import_build_utils24.BUILDER_PRE_DEPLOY_STEP, {
14189
+ preDeployCommand
14190
+ }).trace(async () => {
14191
+ console.log(`Running pre-deploy command: \`${preDeployCommand}\``);
14192
+ await (0, import_build_utils24.execCommand)(preDeployCommand, {
14193
+ env: capturedEnv,
14194
+ cwd: capturedCwd
14195
+ });
14196
+ });
14197
+ });
14198
+ }
14199
+ const vendorDir = resolveVendorDir();
14200
+ let crons;
14201
+ let runtimeTrampoline;
14202
+ if (entrypoint) {
14203
+ (0, import_build_utils24.debug)("Entrypoint is", entrypoint);
14204
+ const moduleName = entrypointToModule(entrypoint);
14205
+ if (handlerFunction) {
14206
+ const entrypointPath = (0, import_path21.join)(workPath, entrypoint);
14207
+ const source = await import_fs20.default.promises.readFile(entrypointPath, "utf-8");
14208
+ const found = await (0, import_python_analysis12.containsTopLevelCallable)(source, handlerFunction);
14209
+ if (!found) {
14210
+ throw new import_build_utils24.NowBuildError({
14211
+ code: "PYTHON_HANDLER_NOT_FOUND",
14212
+ message: `Handler function "${handlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
14213
+ });
14214
+ }
14215
+ }
14216
+ const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
14217
+ const entrypointWithSuffix = `${entrypoint}${suffix}`;
14218
+ (0, import_build_utils24.debug)("Entrypoint with suffix is", entrypointWithSuffix);
14219
+ crons = await getServiceCrons({
14220
+ service,
14221
+ entrypoint,
14222
+ rawEntrypoint,
14223
+ handlerFunction,
14224
+ pythonBin: getVenvPythonBin(venvPath),
14225
+ env: pythonEnv,
14226
+ workPath
14227
+ });
14228
+ const extraTrampolineEnv = [];
14229
+ if (crons?.length) {
14230
+ const json = JSON.stringify(buildCronRouteTable(crons));
14231
+ (0, import_assert.default)(!json.includes("\\"), `backslash in cron route table: ${json}`);
14232
+ extraTrampolineEnv.push(`"__VC_CRON_ROUTES": '${json}'`);
14233
+ }
14234
+ const variableName = resolved?.variableName ?? "";
14235
+ runtimeTrampoline = createRuntimeTrampoline({
14236
+ moduleName,
14237
+ entrypoint: entrypointWithSuffix,
14238
+ vendorDir,
14239
+ variableName,
14240
+ extraEnv: extraTrampolineEnv
14241
+ });
14242
+ }
14243
+ const compileAllEnabled = shouldCompileAll({
14244
+ isDev: meta.isDev,
14245
+ hasCustomCommand,
14246
+ // A pre-deploy command can rewrite source after the build, which would make
14247
+ // unchecked-hash precompiled bytecode stale; skip precompilation to avoid serving it.
14248
+ hasPreDeployCommand: typeof preDeployCommand === "string"
14249
+ });
14250
+ const lambdaEnv = {};
14251
+ lambdaEnv.PYTHONPATH = vendorDir;
14252
+ lambdaEnv.PYTHONDONTWRITEBYTECODE = "1";
14253
+ Object.assign(lambdaEnv, quirksResult.env);
14254
+ if (shouldInstallVercelWorkers) {
14255
+ lambdaEnv.VERCEL_HAS_WORKER_SERVICES = "1";
14256
+ }
14257
+ if (queueIntegrations.length > 0) {
14258
+ lambdaEnv.VERCEL_QUEUE_INTEGRATIONS = queueIntegrations.map(
14259
+ ({ module: module2, installer, servingActivator }) => `${module2}:${installer}` + (servingActivator ? `:${servingActivator}` : "")
14260
+ ).join(",");
14261
+ }
14262
+ const apschedulerSubscribers = apschedulerSubscriberIdentities(subscribers);
14263
+ if (apschedulerSubscribers.length > 0) {
14264
+ lambdaEnv.VERCEL_APSCHEDULER_SUBSCRIBERS = JSON.stringify(
14265
+ apschedulerSubscribers
14266
+ );
14267
+ const previewConfig = await getApschedulerPreviewConfig(workPath);
14268
+ if (previewConfig) {
14269
+ lambdaEnv.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS = String(
14270
+ previewConfig.idleTimeoutSeconds
14271
+ );
14272
+ }
14273
+ }
14274
+ const functionConfig = getMatchingFunction(
14275
+ config,
14276
+ entrypoint
14277
+ )?.functionConfig;
14278
+ const includeFilesPatterns = [
14279
+ ...normalizeGlobs(config?.includeFiles),
14280
+ ...normalizeGlobs(functionConfig?.includeFiles)
14281
+ ];
14282
+ const excludeFilesPatterns = [
14283
+ ...normalizeGlobs(config?.excludeFiles),
14284
+ ...normalizeGlobs(functionConfig?.excludeFiles)
14285
+ ];
14286
+ const cdnBundle = getFastAPICdnBundle(
14287
+ fastapiStatic?.collectedMounts ?? [],
14288
+ workPath,
14289
+ pythonPackage.manifest?.data?.tool?.vercel?.fastapi
14290
+ );
14291
+ const initialFiles = {};
14292
+ for (const pattern of includeFilesPatterns) {
14293
+ Object.assign(initialFiles, await (0, import_build_utils24.glob)(pattern, workPath));
14294
+ }
14295
+ Object.assign(initialFiles, cdnBundle.directoryStubs);
14296
+ if (djangoStatic?.manifestRelPath) {
14297
+ initialFiles[djangoStatic.manifestRelPath] = new import_build_utils24.FileFsRef({
14298
+ fsPath: (0, import_path21.join)(workPath, djangoStatic.manifestRelPath)
14299
+ });
14300
+ }
14301
+ const handlerPyFilename = "vc__handler__python";
14302
+ if (config.framework === "fasthtml") {
14303
+ const { SESSKEY = "" } = process.env;
14304
+ initialFiles[".sesskey"] = new import_build_utils24.FileBlob({ data: `"${SESSKEY}"` });
14305
+ }
14306
+ const { pycachePrefix, files } = await packFunctionBundle({
14307
+ builderSpan,
14308
+ workPath,
14309
+ uvLockPath,
14310
+ uvProjectDir,
14311
+ projectName,
14312
+ pythonVersion,
14313
+ hasCustomCommand,
14314
+ alwaysBundlePackages: [
14315
+ ...quirksResult.alwaysBundlePackages ?? [],
14316
+ ...shouldInstallVercelWorkers ? ["vercel-workers", "vercel_workers"] : []
14317
+ ],
14318
+ initialFiles,
14319
+ additionalExcludes: [...excludeFilesPatterns, ...cdnBundle.excludePatterns],
14320
+ compileAllEnabled,
14321
+ compileEnv: pythonEnv,
14322
+ venvPath,
14323
+ entrypoint,
14324
+ importClosureContext: {
14325
+ frameworkSeeds: importSeeds,
14326
+ extraPythonPath: hookResult?.extraPythonPath,
14327
+ subscriberDeclarations,
14328
+ subscribers,
14329
+ workflows,
14330
+ workflowMode
14331
+ },
14332
+ largeFunctionLabel: entrypoint ?? rawEntrypoint
14333
+ });
14334
+ if (pycachePrefix) {
14335
+ lambdaEnv.PYTHONPYCACHEPREFIX = pycachePrefix;
14336
+ }
14238
14337
  let output;
14239
14338
  if (entrypoint && runtimeTrampoline) {
14240
14339
  const webFiles = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "14.2.0",
3
+ "version": "15.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -17,7 +17,7 @@
17
17
  "@vercel/python-analysis": "0.14.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@vercel/build-utils": "14.10.1"
20
+ "@vercel/build-utils": "14.11.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@renovatebot/pep440": "4.2.1",
@@ -38,8 +38,8 @@
38
38
  "smol-toml": "1.5.2",
39
39
  "vitest": "4.1.10",
40
40
  "which": "3.0.0",
41
+ "@vercel/build-utils": "14.11.0",
41
42
  "@vercel/error-utils": "2.2.1",
42
- "@vercel/build-utils": "14.10.1",
43
43
  "@vercel/python-runtime": "0.23.0"
44
44
  },
45
45
  "scripts": {