@vercel/python 25.0.0 → 25.1.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 +272 -94
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -9017,12 +9017,18 @@ async function syncDependencies({
9017
9017
  pythonBin,
9018
9018
  env,
9019
9019
  onStdout,
9020
- onStderr
9020
+ onStderr,
9021
+ onlyGroup,
9022
+ activeVenv
9021
9023
  }) {
9022
9024
  const pythonPackage = await discoverPackage({
9023
9025
  entrypointDir: workPath,
9024
9026
  rootDir: workPath
9025
9027
  });
9028
+ if (onlyGroup && !(onlyGroup in (pythonPackage.manifest?.data?.["dependency-groups"] ?? {}))) {
9029
+ (0, import_build_utils15.debug)(`No "${onlyGroup}" dependency group found, skipping dependency sync`);
9030
+ return;
9031
+ }
9026
9032
  const installInfo = detectInstallSource(pythonPackage, workPath);
9027
9033
  const { manifestType } = installInfo;
9028
9034
  let { manifestPath } = installInfo;
@@ -9064,6 +9070,8 @@ async function syncDependencies({
9064
9070
  uvPath,
9065
9071
  pythonBin,
9066
9072
  env,
9073
+ onlyGroup,
9074
+ activeVenv,
9067
9075
  onStdout: (data) => captured.push(["stdout", data]),
9068
9076
  onStderr: (data) => captured.push(["stderr", data])
9069
9077
  });
@@ -9083,6 +9091,8 @@ async function runSync({
9083
9091
  uvPath,
9084
9092
  pythonBin,
9085
9093
  env,
9094
+ onlyGroup,
9095
+ activeVenv,
9086
9096
  onStdout,
9087
9097
  onStderr
9088
9098
  }) {
@@ -9102,16 +9112,32 @@ async function runSync({
9102
9112
  }
9103
9113
  spawnCmd = uvPath;
9104
9114
  spawnArgs = ["sync"];
9115
+ if (activeVenv) {
9116
+ spawnArgs.push("--active");
9117
+ }
9118
+ if (onlyGroup) {
9119
+ spawnArgs.push("--only-group", onlyGroup, "--no-install-project");
9120
+ }
9105
9121
  break;
9106
9122
  }
9107
9123
  case "pylock.toml": {
9108
9124
  spawnCmd = pip.cmd;
9109
- spawnArgs = [...pip.prefix, "-r", manifestPath];
9125
+ spawnArgs = [...pip.prefix];
9126
+ if (onlyGroup) {
9127
+ spawnArgs.push("--group", onlyGroup);
9128
+ } else {
9129
+ spawnArgs.push("-r", manifestPath);
9130
+ }
9110
9131
  break;
9111
9132
  }
9112
9133
  case "pyproject.toml": {
9113
9134
  spawnCmd = pip.cmd;
9114
- spawnArgs = [...pip.prefix, projectDir];
9135
+ spawnArgs = [...pip.prefix];
9136
+ if (onlyGroup) {
9137
+ spawnArgs.push("--group", onlyGroup);
9138
+ } else {
9139
+ spawnArgs.push(projectDir);
9140
+ }
9115
9141
  break;
9116
9142
  }
9117
9143
  default:
@@ -9364,7 +9390,229 @@ async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath)
9364
9390
  env.PATH = `${binDir}${import_path13.delimiter}${env.PATH || ""}`;
9365
9391
  return { command: pythonBin, args: [] };
9366
9392
  }
9393
+ async function getPersistentServer(serverKey, isStale) {
9394
+ while (true) {
9395
+ const existing = PERSISTENT_SERVERS.get(serverKey);
9396
+ if (existing) {
9397
+ if (isStale?.(existing)) {
9398
+ killPersistentServer(serverKey);
9399
+ } else {
9400
+ return {
9401
+ port: existing.port,
9402
+ pid: existing.pid,
9403
+ shutdown: async () => {
9404
+ }
9405
+ };
9406
+ }
9407
+ }
9408
+ const pending = PENDING_STARTS.get(serverKey);
9409
+ if (!pending)
9410
+ return null;
9411
+ await pending;
9412
+ }
9413
+ }
9414
+ async function spawnPersistentServer({
9415
+ serverKey,
9416
+ workPath,
9417
+ entry,
9418
+ modulePath,
9419
+ shimSubdir,
9420
+ framework,
9421
+ variableName,
9422
+ extraPythonPath,
9423
+ spawnCommand,
9424
+ spawnArgsPrefix,
9425
+ port: requestedPort,
9426
+ env,
9427
+ onStdout,
9428
+ onStderr,
9429
+ scheduleModule,
9430
+ scheduleEnv
9431
+ }) {
9432
+ const port = requestedPort ?? await (0, import_get_port.default)();
9433
+ env.PORT = `${port}`;
9434
+ if (entry) {
9435
+ env.__VC_HANDLER_ENTRYPOINT_ABS = (0, import_path13.join)(workPath, entry);
9436
+ }
9437
+ const devShim = createDevShim(
9438
+ workPath,
9439
+ entry,
9440
+ modulePath,
9441
+ shimSubdir,
9442
+ framework,
9443
+ variableName
9444
+ );
9445
+ if (devShim) {
9446
+ const shimDir = devShim.shimDir || (0, import_path13.join)(workPath, ".vercel", "python");
9447
+ const runtimeDir = (0, import_path13.join)(workPath, ".vercel", "python");
9448
+ const pathParts = shimDir !== runtimeDir ? [shimDir, runtimeDir] : [shimDir];
9449
+ if (devShim.extraPythonPath) {
9450
+ pathParts.push(devShim.extraPythonPath);
9451
+ }
9452
+ if (extraPythonPath) {
9453
+ pathParts.push(extraPythonPath);
9454
+ }
9455
+ const existingPythonPath = env.PYTHONPATH || "";
9456
+ if (existingPythonPath) {
9457
+ pathParts.push(existingPythonPath);
9458
+ }
9459
+ env.PYTHONPATH = pathParts.join(import_path13.delimiter);
9460
+ }
9461
+ const moduleToRun = devShim?.module || modulePath;
9462
+ const pythonArgs = ["-u", "-m", moduleToRun];
9463
+ const argv = [...spawnArgsPrefix, ...pythonArgs];
9464
+ (0, import_build_utils15.debug)(
9465
+ `Starting Python dev server (${framework}): ${spawnCommand} ${argv.join(" ")} [PORT=${port}]`
9466
+ );
9467
+ if (process.stdout.columns) {
9468
+ env.COLUMNS = `${process.stdout.columns}`;
9469
+ }
9470
+ const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
9471
+ cwd: workPath,
9472
+ env,
9473
+ stdio: ["ignore", "pipe", "pipe"],
9474
+ detached: true
9475
+ });
9476
+ const stdoutLogListener = createLogListener(onStdout, process.stdout);
9477
+ const stderrLogListener = createLogListener(onStderr, process.stderr);
9478
+ child.stdout?.on("data", stdoutLogListener);
9479
+ child.stderr?.on("data", stderrLogListener);
9480
+ const childExited = new Promise((_resolve, reject) => {
9481
+ child.once("error", (err) => {
9482
+ reject(err);
9483
+ });
9484
+ child.once("exit", (code, signal) => {
9485
+ reject(
9486
+ new Error(
9487
+ `Python dev server exited before binding (code=${code}, signal=${signal})`
9488
+ )
9489
+ );
9490
+ });
9491
+ });
9492
+ await Promise.race([
9493
+ checkForPort(port, DEV_SERVER_STARTUP_TIMEOUT),
9494
+ childExited
9495
+ ]);
9496
+ const pid = child.pid;
9497
+ PERSISTENT_SERVERS.set(serverKey, {
9498
+ port,
9499
+ pid,
9500
+ child,
9501
+ stdoutLogListener,
9502
+ stderrLogListener,
9503
+ scheduleModule,
9504
+ scheduleEnv
9505
+ });
9506
+ return { port, pid };
9507
+ }
9508
+ var PROXY_DEPENDENCY_GROUP = "vercel.proxy";
9509
+ var startProxyDevServer = async ({
9510
+ entrypoint,
9511
+ workPath,
9512
+ meta = {},
9513
+ config,
9514
+ onStdout,
9515
+ onStderr
9516
+ }) => {
9517
+ const handlerFunction = config?.handlerFunction;
9518
+ if (typeof handlerFunction !== "string" || !handlerFunction) {
9519
+ throw new Error(
9520
+ 'Python proxy dev server requires "config.handlerFunction" to be set'
9521
+ );
9522
+ }
9523
+ const serverKey = `${workPath}::proxy::${entrypoint}`;
9524
+ const existing = await getPersistentServer(serverKey);
9525
+ if (existing) {
9526
+ return existing;
9527
+ }
9528
+ if (!restoreWarnings)
9529
+ restoreWarnings = silenceNodeWarnings();
9530
+ installGlobalCleanupHandlers();
9531
+ const proxySource = (0, import_fs13.readFileSync)((0, import_path13.join)(workPath, entrypoint), "utf8");
9532
+ if (!await (0, import_python_analysis8.containsTopLevelCallable)(proxySource, handlerFunction)) {
9533
+ throw new import_build_utils15.NowBuildError({
9534
+ code: "PYTHON_HANDLER_NOT_FOUND",
9535
+ message: `Handler function "${handlerFunction}" not found in ${entrypoint}. Ensure it is defined at the module's top level.`
9536
+ });
9537
+ }
9538
+ const env = { ...process.env, ...meta.env || {} };
9539
+ let resolveChildReady;
9540
+ let rejectChildReady;
9541
+ const childReady = new Promise(
9542
+ (resolve4, reject) => {
9543
+ resolveChildReady = resolve4;
9544
+ rejectChildReady = reject;
9545
+ }
9546
+ );
9547
+ PENDING_STARTS.set(serverKey, childReady);
9548
+ try {
9549
+ const { pythonPath: systemPython } = getDefaultPythonVersion(meta);
9550
+ const uvPath = await findUvBinary(systemPython);
9551
+ if (uvPath) {
9552
+ checkUvBinaryVersion(uvPath);
9553
+ }
9554
+ const venvPath = (0, import_path13.join)(
9555
+ workPath,
9556
+ ".vercel",
9557
+ "python",
9558
+ PROXY_DEPENDENCY_GROUP,
9559
+ ".venv"
9560
+ );
9561
+ await ensureVenv({
9562
+ pythonVersion: { pythonPath: systemPython },
9563
+ venvPath,
9564
+ uvPath,
9565
+ quiet: true
9566
+ });
9567
+ const pythonCmd = getVenvPythonBin(venvPath);
9568
+ env.VIRTUAL_ENV = venvPath;
9569
+ env.PATH = `${getVenvBinDir(venvPath)}${import_path13.delimiter}${env.PATH || ""}`;
9570
+ await syncDependencies({
9571
+ workPath,
9572
+ uvPath,
9573
+ pythonBin: pythonCmd,
9574
+ env,
9575
+ onStdout,
9576
+ onStderr,
9577
+ onlyGroup: PROXY_DEPENDENCY_GROUP,
9578
+ activeVenv: true
9579
+ });
9580
+ await installInjectedDevPackage(
9581
+ {
9582
+ name: "vercel-runtime",
9583
+ pinnedVersion: VERCEL_RUNTIME_VERSION,
9584
+ envOverride: env.VERCEL_RUNTIME_PYTHON
9585
+ },
9586
+ { workPath, uvPath, pythonBin: pythonCmd, env, onStdout, onStderr }
9587
+ );
9588
+ const { port, pid } = await spawnPersistentServer({
9589
+ serverKey,
9590
+ workPath,
9591
+ entry: entrypoint,
9592
+ modulePath: entrypointToModule(entrypoint),
9593
+ shimSubdir: PROXY_DEPENDENCY_GROUP,
9594
+ framework: "python",
9595
+ variableName: handlerFunction,
9596
+ spawnCommand: pythonCmd,
9597
+ spawnArgsPrefix: [],
9598
+ env,
9599
+ onStdout,
9600
+ onStderr
9601
+ });
9602
+ resolveChildReady({ port, pid });
9603
+ return { port, pid, shutdown: async () => {
9604
+ } };
9605
+ } catch (err) {
9606
+ rejectChildReady(err);
9607
+ throw err;
9608
+ } finally {
9609
+ PENDING_STARTS.delete(serverKey);
9610
+ }
9611
+ };
9367
9612
  var startDevServer = async (opts) => {
9613
+ if (opts.config?.middleware === true) {
9614
+ return startProxyDevServer(opts);
9615
+ }
9368
9616
  const {
9369
9617
  entrypoint: rawEntrypoint,
9370
9618
  workPath,
@@ -9382,24 +9630,12 @@ var startDevServer = async (opts) => {
9382
9630
  const scheduleEnv = scheduleFunction ? { ...env } : void 0;
9383
9631
  const serviceName = service?.name ?? (typeof meta.serviceName === "string" ? meta.serviceName : void 0);
9384
9632
  const serverKey = scheduleFunction ? `${workPath}::schedule::${scheduleFunction.outputPath}` : serviceName ? `${workPath}::${framework}::${serviceName}` : `${workPath}::${framework}`;
9385
- while (true) {
9386
- const existing = PERSISTENT_SERVERS.get(serverKey);
9387
- if (existing) {
9388
- if (scheduleFunction && (existing.scheduleModule !== scheduleModule || !(0, import_util2.isDeepStrictEqual)(existing.scheduleEnv, scheduleEnv))) {
9389
- killPersistentServer(serverKey);
9390
- } else {
9391
- return {
9392
- port: existing.port,
9393
- pid: existing.pid,
9394
- shutdown: async () => {
9395
- }
9396
- };
9397
- }
9398
- }
9399
- const pending = PENDING_STARTS.get(serverKey);
9400
- if (!pending)
9401
- break;
9402
- await pending;
9633
+ const existing = await getPersistentServer(
9634
+ serverKey,
9635
+ (info) => !!scheduleFunction && (info.scheduleModule !== scheduleModule || !(0, import_util2.isDeepStrictEqual)(info.scheduleEnv, scheduleEnv))
9636
+ );
9637
+ if (existing) {
9638
+ return existing;
9403
9639
  }
9404
9640
  if (!framework && !scheduleFunction) {
9405
9641
  return null;
@@ -9461,9 +9697,6 @@ var startDevServer = async (opts) => {
9461
9697
  }
9462
9698
  const { entrypoint: entry, variableName } = resolved;
9463
9699
  const modulePath = scheduleHandlerPath ? generatedPythonPathToModule(scheduleHandlerPath) : entrypointToModule(entry);
9464
- let childProcess = null;
9465
- let stdoutLogListener = null;
9466
- let stderrLogListener = null;
9467
9700
  let resolveChildReady;
9468
9701
  let rejectChildReady;
9469
9702
  const childReady = new Promise(
@@ -9713,82 +9946,25 @@ If you are using a virtual environment, activate it before running "vercel dev",
9713
9946
  if (crons?.length) {
9714
9947
  env.__VC_CRON_ROUTES = JSON.stringify(buildCronRouteTable(crons));
9715
9948
  }
9716
- const port = typeof meta.port === "number" ? meta.port : await (0, import_get_port.default)();
9717
- env.PORT = `${port}`;
9718
- if (entry) {
9719
- env.__VC_HANDLER_ENTRYPOINT_ABS = (0, import_path13.join)(workPath, entry);
9720
- }
9721
- const devShim = createDevShim(
9949
+ const { port, pid } = await spawnPersistentServer({
9950
+ serverKey,
9722
9951
  workPath,
9723
9952
  entry,
9724
9953
  modulePath,
9725
- scheduleHandlerPath ? scheduleHandlerPath.replace(/\.py$/, "") : serviceName && import_path13.posix.join("services", serviceName),
9726
- framework ?? "python",
9727
- variableName ?? ""
9728
- );
9729
- if (devShim) {
9730
- const shimDir = devShim.shimDir || (0, import_path13.join)(workPath, ".vercel", "python");
9731
- const runtimeDir = (0, import_path13.join)(workPath, ".vercel", "python");
9732
- const pathParts = shimDir !== runtimeDir ? [shimDir, runtimeDir] : [shimDir];
9733
- if (devShim.extraPythonPath) {
9734
- pathParts.push(devShim.extraPythonPath);
9735
- }
9736
- if (hookResult?.extraPythonPath) {
9737
- pathParts.push(hookResult.extraPythonPath);
9738
- }
9739
- const existingPythonPath = env.PYTHONPATH || "";
9740
- if (existingPythonPath) {
9741
- pathParts.push(existingPythonPath);
9742
- }
9743
- env.PYTHONPATH = pathParts.join(import_path13.delimiter);
9744
- }
9745
- const moduleToRun = devShim?.module || modulePath;
9746
- const pythonArgs = ["-u", "-m", moduleToRun];
9747
- const argv = [...spawnArgsPrefix, ...pythonArgs];
9748
- (0, import_build_utils15.debug)(
9749
- `Starting Python dev server (${framework}): ${spawnCommand} ${argv.join(" ")} [PORT=${port}]`
9750
- );
9751
- if (process.stdout.columns) {
9752
- env.COLUMNS = `${process.stdout.columns}`;
9753
- }
9754
- const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
9755
- cwd: workPath,
9954
+ shimSubdir: scheduleHandlerPath ? scheduleHandlerPath.replace(/\.py$/, "") : serviceName && import_path13.posix.join("services", serviceName),
9955
+ framework: framework ?? "python",
9956
+ variableName: variableName ?? "",
9957
+ extraPythonPath: hookResult?.extraPythonPath,
9958
+ spawnCommand,
9959
+ spawnArgsPrefix,
9960
+ port: typeof meta.port === "number" ? meta.port : void 0,
9756
9961
  env,
9757
- stdio: ["ignore", "pipe", "pipe"],
9758
- detached: true
9759
- });
9760
- childProcess = child;
9761
- stdoutLogListener = createLogListener(onStdout, process.stdout);
9762
- stderrLogListener = createLogListener(onStderr, process.stderr);
9763
- child.stdout?.on("data", stdoutLogListener);
9764
- child.stderr?.on("data", stderrLogListener);
9765
- const childExited = new Promise((_resolve, reject) => {
9766
- child.once("error", (err) => {
9767
- reject(err);
9768
- });
9769
- child.once("exit", (code, signal) => {
9770
- reject(
9771
- new Error(
9772
- `Python dev server exited before binding (code=${code}, signal=${signal})`
9773
- )
9774
- );
9775
- });
9776
- });
9777
- await Promise.race([
9778
- checkForPort(port, DEV_SERVER_STARTUP_TIMEOUT),
9779
- childExited
9780
- ]);
9781
- const pid = child.pid;
9782
- resolveChildReady({ port, pid });
9783
- PERSISTENT_SERVERS.set(serverKey, {
9784
- port,
9785
- pid,
9786
- child: childProcess,
9787
- stdoutLogListener,
9788
- stderrLogListener,
9962
+ onStdout,
9963
+ onStderr,
9789
9964
  scheduleModule,
9790
9965
  scheduleEnv
9791
9966
  });
9967
+ resolveChildReady({ port, pid });
9792
9968
  const shutdown = async () => {
9793
9969
  };
9794
9970
  return { port, pid, shutdown, crons, queueSubscriptions };
@@ -12051,7 +12227,9 @@ async function buildProxyMiddleware({
12051
12227
  // Install only the proxy's dependency group into the proxy venv;
12052
12228
  // the project itself is neither built nor installed.
12053
12229
  // If the group is absent the proxy has no extra deps — skip sync.
12054
- extraSyncParams: "vercel.proxy" in (pythonPackage?.manifest?.data?.["dependency-groups"] ?? {}) ? { onlyGroup: "vercel.proxy", noInstallProject: true } : void 0
12230
+ // Group names are normalized on read, so `vercel.proxy` is keyed
12231
+ // as `vercel-proxy`.
12232
+ extraSyncParams: "vercel-proxy" in (pythonPackage?.manifest?.data?.["dependency-groups"] ?? {}) ? { onlyGroup: "vercel.proxy", noInstallProject: true } : void 0
12055
12233
  })
12056
12234
  );
12057
12235
  const pipPlatformArgs = target.uvPlatform ? ["--python-platform", target.uvPlatform] : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "25.0.0",
3
+ "version": "25.1.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -14,10 +14,10 @@
14
14
  "directory": "packages/python"
15
15
  },
16
16
  "dependencies": {
17
- "@vercel/python-analysis": "0.14.0"
17
+ "@vercel/python-analysis": "0.15.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@vercel/build-utils": "14.21.0"
20
+ "@vercel/build-utils": "14.21.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@renovatebot/pep440": "4.2.1",
@@ -37,8 +37,8 @@
37
37
  "smol-toml": "1.5.2",
38
38
  "vitest": "4.1.10",
39
39
  "which": "3.0.0",
40
- "@vercel/build-utils": "14.21.0",
41
40
  "@vercel/error-utils": "2.2.1",
41
+ "@vercel/build-utils": "14.21.1",
42
42
  "@vercel/python-runtime": "0.23.2"
43
43
  },
44
44
  "scripts": {