@vercel/python 6.10.0 → 6.11.1

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 +206 -291
  2. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -137,12 +137,12 @@ var require_isexe = __commonJS({
137
137
  if (typeof Promise !== "function") {
138
138
  throw new TypeError("callback not provided");
139
139
  }
140
- return new Promise(function(resolve2, reject) {
140
+ return new Promise(function(resolve, reject) {
141
141
  isexe(path, options || {}, function(er, is) {
142
142
  if (er) {
143
143
  reject(er);
144
144
  } else {
145
- resolve2(is);
145
+ resolve(is);
146
146
  }
147
147
  });
148
148
  });
@@ -1498,7 +1498,7 @@ var require_parse = __commonJS({
1498
1498
  var escape = require_escape();
1499
1499
  var readShebang = require_readShebang();
1500
1500
  var semver = require_semver();
1501
- var isWin4 = process.platform === "win32";
1501
+ var isWin3 = process.platform === "win32";
1502
1502
  var isExecutableRegExp = /\.(?:com|exe)$/i;
1503
1503
  var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
1504
1504
  var supportsShellOption = niceTry(() => semver.satisfies(process.version, "^4.8.0 || ^5.7.0 || >= 6.0.0", true)) || false;
@@ -1513,7 +1513,7 @@ var require_parse = __commonJS({
1513
1513
  return parsed.file;
1514
1514
  }
1515
1515
  function parseNonShell(parsed) {
1516
- if (!isWin4) {
1516
+ if (!isWin3) {
1517
1517
  return parsed;
1518
1518
  }
1519
1519
  const commandFile = detectShebang(parsed);
@@ -1535,7 +1535,7 @@ var require_parse = __commonJS({
1535
1535
  return parsed;
1536
1536
  }
1537
1537
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
1538
- if (isWin4) {
1538
+ if (isWin3) {
1539
1539
  parsed.command = typeof parsed.options.shell === "string" ? parsed.options.shell : process.env.comspec || "cmd.exe";
1540
1540
  parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`];
1541
1541
  parsed.options.windowsVerbatimArguments = true;
@@ -1578,7 +1578,7 @@ var require_parse = __commonJS({
1578
1578
  var require_enoent = __commonJS({
1579
1579
  "../../node_modules/.pnpm/cross-spawn@6.0.5/node_modules/cross-spawn/lib/enoent.js"(exports, module2) {
1580
1580
  "use strict";
1581
- var isWin4 = process.platform === "win32";
1581
+ var isWin3 = process.platform === "win32";
1582
1582
  function notFoundError(original, syscall) {
1583
1583
  return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
1584
1584
  code: "ENOENT",
@@ -1589,7 +1589,7 @@ var require_enoent = __commonJS({
1589
1589
  });
1590
1590
  }
1591
1591
  function hookChildProcess(cp, parsed) {
1592
- if (!isWin4) {
1592
+ if (!isWin3) {
1593
1593
  return;
1594
1594
  }
1595
1595
  const originalEmit = cp.emit;
@@ -1604,13 +1604,13 @@ var require_enoent = __commonJS({
1604
1604
  };
1605
1605
  }
1606
1606
  function verifyENOENT(status, parsed) {
1607
- if (isWin4 && status === 1 && !parsed.file) {
1607
+ if (isWin3 && status === 1 && !parsed.file) {
1608
1608
  return notFoundError(parsed.original, "spawn");
1609
1609
  }
1610
1610
  return null;
1611
1611
  }
1612
1612
  function verifyENOENTSync(status, parsed) {
1613
- if (isWin4 && status === 1 && !parsed.file) {
1613
+ if (isWin3 && status === 1 && !parsed.file) {
1614
1614
  return notFoundError(parsed.original, "spawnSync");
1615
1615
  }
1616
1616
  return null;
@@ -2042,7 +2042,7 @@ var require_get_stream = __commonJS({
2042
2042
  options = Object.assign({ maxBuffer: Infinity }, options);
2043
2043
  const { maxBuffer } = options;
2044
2044
  let stream;
2045
- return new Promise((resolve2, reject) => {
2045
+ return new Promise((resolve, reject) => {
2046
2046
  const rejectPromise = (error) => {
2047
2047
  if (error) {
2048
2048
  error.bufferedData = stream.getBufferedValue();
@@ -2054,7 +2054,7 @@ var require_get_stream = __commonJS({
2054
2054
  rejectPromise(error);
2055
2055
  return;
2056
2056
  }
2057
- resolve2();
2057
+ resolve();
2058
2058
  });
2059
2059
  stream.on("data", () => {
2060
2060
  if (stream.getBufferedLength() > maxBuffer) {
@@ -2078,11 +2078,11 @@ var require_p_finally = __commonJS({
2078
2078
  onFinally = onFinally || (() => {
2079
2079
  });
2080
2080
  return promise.then(
2081
- (val) => new Promise((resolve2) => {
2082
- resolve2(onFinally());
2081
+ (val) => new Promise((resolve) => {
2082
+ resolve(onFinally());
2083
2083
  }).then(() => val),
2084
- (err) => new Promise((resolve2) => {
2085
- resolve2(onFinally());
2084
+ (err) => new Promise((resolve) => {
2085
+ resolve(onFinally());
2086
2086
  }).then(() => {
2087
2087
  throw err;
2088
2088
  })
@@ -2143,7 +2143,7 @@ var require_signal_exit = __commonJS({
2143
2143
  } else {
2144
2144
  assert = require("assert");
2145
2145
  signals = require_signals();
2146
- isWin4 = /^win/i.test(process2.platform);
2146
+ isWin3 = /^win/i.test(process2.platform);
2147
2147
  EE = require("events");
2148
2148
  if (typeof EE !== "function") {
2149
2149
  EE = EE.EventEmitter;
@@ -2215,7 +2215,7 @@ var require_signal_exit = __commonJS({
2215
2215
  unload();
2216
2216
  emit("exit", null, sig);
2217
2217
  emit("afterexit", null, sig);
2218
- if (isWin4 && sig === "SIGHUP") {
2218
+ if (isWin3 && sig === "SIGHUP") {
2219
2219
  sig = "SIGINT";
2220
2220
  }
2221
2221
  process2.kill(process2.pid, sig);
@@ -2272,7 +2272,7 @@ var require_signal_exit = __commonJS({
2272
2272
  }
2273
2273
  var assert;
2274
2274
  var signals;
2275
- var isWin4;
2275
+ var isWin3;
2276
2276
  var EE;
2277
2277
  var emitter;
2278
2278
  var unload;
@@ -2461,8 +2461,8 @@ var require_execa = __commonJS({
2461
2461
  }
2462
2462
  let ret;
2463
2463
  if (!buffer) {
2464
- ret = new Promise((resolve2, reject) => {
2465
- process2[stream].once("end", resolve2).once("error", reject);
2464
+ ret = new Promise((resolve, reject) => {
2465
+ process2[stream].once("end", resolve).once("error", reject);
2466
2466
  });
2467
2467
  } else if (encoding) {
2468
2468
  ret = _getStream(process2[stream], {
@@ -2551,19 +2551,19 @@ ${stderr}${stdout}`;
2551
2551
  spawned.kill(parsed.opts.killSignal);
2552
2552
  }, parsed.opts.timeout);
2553
2553
  }
2554
- const processDone = new Promise((resolve2) => {
2554
+ const processDone = new Promise((resolve) => {
2555
2555
  spawned.on("exit", (code, signal) => {
2556
2556
  cleanup();
2557
- resolve2({ code, signal });
2557
+ resolve({ code, signal });
2558
2558
  });
2559
2559
  spawned.on("error", (err) => {
2560
2560
  cleanup();
2561
- resolve2({ error: err });
2561
+ resolve({ error: err });
2562
2562
  });
2563
2563
  if (spawned.stdin) {
2564
2564
  spawned.stdin.on("error", (err) => {
2565
2565
  cleanup();
2566
- resolve2({ error: err });
2566
+ resolve({ error: err });
2567
2567
  });
2568
2568
  }
2569
2569
  });
@@ -2651,7 +2651,7 @@ ${stderr}${stdout}`;
2651
2651
  var require_lib = __commonJS({
2652
2652
  "../../node_modules/.pnpm/which@3.0.0/node_modules/which/lib/index.js"(exports, module2) {
2653
2653
  var isexe = require_isexe();
2654
- var { join: join7, delimiter, sep, posix } = require("path");
2654
+ var { join: join7, delimiter: delimiter2, sep, posix } = require("path");
2655
2655
  var isWindows = process.platform === "win32";
2656
2656
  var rSlash = new RegExp(`[${posix.sep}${sep === posix.sep ? "" : sep}]`.replace(/(\\)/g, "\\$1"));
2657
2657
  var rRel = new RegExp(`^\\.${rSlash.source}`);
@@ -2659,7 +2659,7 @@ var require_lib = __commonJS({
2659
2659
  var getPathInfo = (cmd, {
2660
2660
  path: optPath = process.env.PATH,
2661
2661
  pathExt: optPathExt = process.env.PATHEXT,
2662
- delimiter: optDelimiter = delimiter
2662
+ delimiter: optDelimiter = delimiter2
2663
2663
  }) => {
2664
2664
  const pathEnv = cmd.match(rSlash) ? [""] : [
2665
2665
  // windows always checks the cwd first
@@ -2761,9 +2761,9 @@ var import_build_utils8 = require("@vercel/build-utils");
2761
2761
  // src/install.ts
2762
2762
  var import_execa3 = __toESM(require_execa());
2763
2763
  var import_fs3 = __toESM(require("fs"));
2764
- var import_os2 = __toESM(require("os"));
2765
2764
  var import_path4 = require("path");
2766
2765
  var import_build_utils4 = require("@vercel/build-utils");
2766
+ var import_python_analysis = require("@vercel/python-analysis");
2767
2767
 
2768
2768
  // src/utils.ts
2769
2769
  var import_fs2 = __toESM(require("fs"));
@@ -2856,9 +2856,17 @@ var UvRunner = class {
2856
2856
  env: getProtectedUvEnv(process.env)
2857
2857
  });
2858
2858
  } catch (err) {
2859
- throw new Error(
2859
+ const error = new Error(
2860
2860
  `Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
2861
2861
  );
2862
+ if (err && typeof err === "object") {
2863
+ if ("code" in err) {
2864
+ error.code = err.code;
2865
+ } else if ("signal" in err) {
2866
+ error.code = err.signal;
2867
+ }
2868
+ }
2869
+ throw error;
2862
2870
  }
2863
2871
  }
2864
2872
  async addDependencies(options) {
@@ -3061,7 +3069,8 @@ function createVenvEnv(venvPath, baseEnv = process.env) {
3061
3069
  }
3062
3070
  async function ensureVenv({
3063
3071
  pythonPath,
3064
- venvPath
3072
+ venvPath,
3073
+ uvPath
3065
3074
  }) {
3066
3075
  const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
3067
3076
  try {
@@ -3071,7 +3080,11 @@ async function ensureVenv({
3071
3080
  }
3072
3081
  await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
3073
3082
  console.log(`Creating virtual environment at "${venvPath}"...`);
3074
- await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
3083
+ if (uvPath) {
3084
+ await (0, import_execa2.default)(uvPath, ["venv", venvPath]);
3085
+ } else {
3086
+ await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
3087
+ }
3075
3088
  }
3076
3089
  function getVenvPythonBin(venvPath) {
3077
3090
  return (0, import_path3.join)(getVenvBinDir(venvPath), isWin2 ? "python.exe" : "python");
@@ -3379,7 +3392,6 @@ function isInstalled({ version: version2 }) {
3379
3392
  }
3380
3393
 
3381
3394
  // src/install.ts
3382
- var isWin3 = process.platform === "win32";
3383
3395
  var makeDependencyCheckCode = (dependency) => `
3384
3396
  from importlib import util
3385
3397
  dep = '${dependency}'.replace('-', '_')
@@ -3455,225 +3467,130 @@ function resolveVendorDir() {
3455
3467
  const vendorDir = process.env.VERCEL_PYTHON_VENDOR_DIR || "_vendor";
3456
3468
  return vendorDir;
3457
3469
  }
3470
+ function toBuildError(error) {
3471
+ return new import_build_utils4.NowBuildError({
3472
+ code: error.code,
3473
+ message: error.message,
3474
+ link: error.link,
3475
+ action: error.action
3476
+ });
3477
+ }
3458
3478
  async function detectInstallSource({
3459
3479
  workPath,
3460
3480
  entryDirectory,
3461
- fsFiles
3481
+ repoRootPath
3462
3482
  }) {
3463
- const uvLockDir = findDir({
3464
- file: "uv.lock",
3465
- entryDirectory,
3466
- workPath,
3467
- fsFiles
3468
- });
3469
- const pyprojectDir = findDir({
3470
- file: "pyproject.toml",
3471
- entryDirectory,
3472
- workPath,
3473
- fsFiles
3474
- });
3475
- const pipfileLockDir = findDir({
3476
- file: "Pipfile.lock",
3477
- entryDirectory,
3478
- workPath,
3479
- fsFiles
3480
- });
3481
- const pipfileDir = findDir({
3482
- file: "Pipfile",
3483
- entryDirectory,
3484
- workPath,
3485
- fsFiles
3486
- });
3487
- const requirementsDir = findDir({
3488
- file: "requirements.txt",
3489
- entryDirectory,
3490
- workPath,
3491
- fsFiles
3492
- });
3493
- let manifestPath = null;
3494
- let manifestType = null;
3495
- if (uvLockDir && pyprojectDir) {
3496
- manifestType = "uv.lock";
3497
- manifestPath = (0, import_path4.join)(uvLockDir, "uv.lock");
3498
- } else if (pyprojectDir) {
3499
- manifestType = "pyproject.toml";
3500
- manifestPath = (0, import_path4.join)(pyprojectDir, "pyproject.toml");
3501
- } else if (pipfileLockDir) {
3502
- manifestType = "Pipfile.lock";
3503
- manifestPath = (0, import_path4.join)(pipfileLockDir, "Pipfile.lock");
3504
- } else if (pipfileDir) {
3505
- manifestType = "Pipfile";
3506
- manifestPath = (0, import_path4.join)(pipfileDir, "Pipfile");
3507
- } else if (requirementsDir) {
3508
- manifestType = "requirements.txt";
3509
- manifestPath = (0, import_path4.join)(requirementsDir, "requirements.txt");
3510
- }
3511
- let manifestContent;
3512
- if (manifestPath) {
3513
- try {
3514
- manifestContent = await import_fs3.default.promises.readFile(manifestPath, "utf8");
3515
- } catch (err) {
3516
- (0, import_build_utils4.debug)("Failed to read install manifest contents", err);
3483
+ const entrypointDir = (0, import_path4.join)(workPath, entryDirectory);
3484
+ const rootDir = repoRootPath ?? workPath;
3485
+ let pythonPackage;
3486
+ try {
3487
+ pythonPackage = await (0, import_python_analysis.discoverPythonPackage)({
3488
+ entrypointDir,
3489
+ rootDir
3490
+ });
3491
+ } catch (error) {
3492
+ if (error instanceof import_python_analysis.PythonAnalysisError) {
3493
+ throw toBuildError(error);
3517
3494
  }
3495
+ throw error;
3518
3496
  }
3519
- return { manifestPath, manifestType, manifestContent };
3520
- }
3521
- async function createPyprojectToml({
3522
- projectName,
3523
- pyprojectPath,
3524
- dependencies,
3525
- pythonVersion
3526
- }) {
3527
- const version2 = pythonVersion ?? DEFAULT_PYTHON_VERSION;
3528
- const requiresPython = `~=${version2}.0`;
3529
- const depsToml = dependencies.length > 0 ? [
3530
- "dependencies = [",
3531
- ...dependencies.map((dep) => ` "${dep}",`),
3532
- "]"
3533
- ].join("\n") : "dependencies = []";
3534
- const content = [
3535
- "[project]",
3536
- `name = "${projectName}"`,
3537
- 'version = "0.1.0"',
3538
- `requires-python = "${requiresPython}"`,
3539
- "classifiers = [",
3540
- ' "Private :: Do Not Upload",',
3541
- "]",
3542
- depsToml,
3543
- ""
3544
- ].join("\n");
3545
- await import_fs3.default.promises.writeFile(pyprojectPath, content);
3546
- }
3547
- function findUvLockUpwards(startDir, repoRootPath) {
3548
- const start = (0, import_path4.resolve)(startDir);
3549
- const base = repoRootPath ? (0, import_path4.resolve)(repoRootPath) : void 0;
3550
- for (const dir of (0, import_build_utils4.traverseUpDirectories)({ start, base })) {
3551
- const lockPath = (0, import_path4.join)(dir, "uv.lock");
3552
- const pyprojectPath = (0, import_path4.join)(dir, "pyproject.toml");
3553
- if (import_fs3.default.existsSync(lockPath) && import_fs3.default.existsSync(pyprojectPath)) {
3554
- return lockPath;
3555
- }
3497
+ let manifestType = null;
3498
+ let manifestPath = null;
3499
+ const lockFile = pythonPackage.manifest?.lockFile ?? pythonPackage.workspaceLockFile;
3500
+ if (lockFile) {
3501
+ if (lockFile.kind === import_python_analysis.PythonLockFileKind.UvLock) {
3502
+ manifestType = "uv.lock";
3503
+ manifestPath = (0, import_path4.join)(rootDir, lockFile.path);
3504
+ } else if (lockFile.kind === import_python_analysis.PythonLockFileKind.PylockToml) {
3505
+ manifestType = "pylock.toml";
3506
+ manifestPath = (0, import_path4.join)(rootDir, lockFile.path);
3507
+ }
3508
+ } else if (pythonPackage.manifest) {
3509
+ manifestType = "pyproject.toml";
3510
+ manifestPath = (0, import_path4.join)(rootDir, pythonPackage.manifest.path);
3556
3511
  }
3557
- return null;
3512
+ return { manifestPath, manifestType, pythonPackage };
3558
3513
  }
3559
3514
  async function ensureUvProject({
3560
3515
  workPath,
3561
3516
  entryDirectory,
3562
- fsFiles,
3563
3517
  repoRootPath,
3564
- pythonPath,
3565
- pipPath,
3566
3518
  pythonVersion,
3567
- uv,
3568
- venvPath,
3569
- meta
3519
+ uv
3570
3520
  }) {
3571
- const uvPath = uv.getPath();
3521
+ const rootDir = repoRootPath ?? workPath;
3572
3522
  const installInfo = await detectInstallSource({
3573
3523
  workPath,
3574
3524
  entryDirectory,
3575
- fsFiles
3525
+ repoRootPath
3576
3526
  });
3577
- const { manifestType, manifestPath } = installInfo;
3527
+ const { manifestType, pythonPackage } = installInfo;
3528
+ const manifest = pythonPackage?.manifest;
3578
3529
  let projectDir;
3579
3530
  let pyprojectPath;
3580
3531
  let lockPath = null;
3581
- if (manifestType === "uv.lock") {
3582
- if (!manifestPath) {
3583
- throw new Error("Expected uv.lock path to be resolved, but it was null");
3532
+ if (manifestType === "uv.lock" || manifestType === "pylock.toml") {
3533
+ const lockFile = pythonPackage?.manifest?.lockFile ?? pythonPackage?.workspaceLockFile;
3534
+ if (!lockFile) {
3535
+ throw new Error(
3536
+ `Expected lock file path to be resolved, but it was null`
3537
+ );
3584
3538
  }
3585
- projectDir = (0, import_path4.dirname)(manifestPath);
3539
+ lockPath = (0, import_path4.join)(rootDir, lockFile.path);
3540
+ projectDir = (0, import_path4.dirname)(lockPath);
3586
3541
  pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
3587
3542
  if (!import_fs3.default.existsSync(pyprojectPath)) {
3588
3543
  throw new Error(
3589
- `Expected "pyproject.toml" next to "uv.lock" in "${projectDir}"`
3544
+ `Expected "pyproject.toml" next to "${lockFile.kind}" in "${projectDir}"`
3590
3545
  );
3591
3546
  }
3592
- lockPath = manifestPath;
3593
- console.log("Installing required dependencies from uv.lock...");
3594
- } else if (manifestType === "pyproject.toml") {
3595
- if (!manifestPath) {
3596
- throw new Error(
3597
- "Expected pyproject.toml path to be resolved, but it was null"
3547
+ console.log(`Installing required dependencies from ${lockFile.kind}...`);
3548
+ } else if (manifest) {
3549
+ projectDir = (0, import_path4.join)(rootDir, (0, import_path4.dirname)(manifest.path));
3550
+ pyprojectPath = (0, import_path4.join)(rootDir, manifest.path);
3551
+ const originKind = manifest.origin?.kind;
3552
+ if (originKind === import_python_analysis.PythonManifestConvertedKind.Pipfile) {
3553
+ console.log("Installing required dependencies from Pipfile...");
3554
+ } else if (originKind === import_python_analysis.PythonManifestConvertedKind.PipfileLock) {
3555
+ console.log("Installing required dependencies from Pipfile.lock...");
3556
+ } else if (originKind === import_python_analysis.PythonManifestConvertedKind.RequirementsTxt || originKind === import_python_analysis.PythonManifestConvertedKind.RequirementsIn) {
3557
+ console.log(
3558
+ `Installing required dependencies from ${manifest.origin?.path ?? "requirements.txt"}...`
3598
3559
  );
3599
- }
3600
- projectDir = (0, import_path4.dirname)(manifestPath);
3601
- pyprojectPath = manifestPath;
3602
- console.log("Installing required dependencies from pyproject.toml...");
3603
- const workspaceLock = findUvLockUpwards(projectDir, repoRootPath);
3604
- if (workspaceLock) {
3605
- lockPath = workspaceLock;
3606
3560
  } else {
3607
- await uv.lock(projectDir);
3608
- }
3609
- } else if (manifestType === "Pipfile.lock" || manifestType === "Pipfile") {
3610
- if (!manifestPath) {
3611
- throw new Error(
3612
- "Expected Pipfile/Pipfile.lock path to be resolved, but it was null"
3613
- );
3614
- }
3615
- projectDir = (0, import_path4.dirname)(manifestPath);
3616
- console.log(`Installing required dependencies from ${manifestType}...`);
3617
- const exportedReq = await exportRequirementsFromPipfile({
3618
- pythonPath,
3619
- pipPath,
3620
- uvPath,
3621
- projectDir,
3622
- meta
3623
- });
3624
- pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
3625
- if (!import_fs3.default.existsSync(pyprojectPath)) {
3626
- await createPyprojectToml({
3627
- projectName: "app",
3628
- pyprojectPath,
3629
- dependencies: [],
3630
- pythonVersion
3631
- });
3561
+ console.log("Installing required dependencies from pyproject.toml...");
3632
3562
  }
3633
- await uv.addFromFile({
3634
- venvPath,
3635
- projectDir,
3636
- requirementsPath: exportedReq
3637
- });
3638
- } else if (manifestType === "requirements.txt") {
3639
- if (!manifestPath) {
3640
- throw new Error(
3641
- "Expected requirements.txt path to be resolved, but it was null"
3642
- );
3563
+ if (manifest.origin) {
3564
+ if (manifest.data.project && !manifest.data.project["requires-python"]) {
3565
+ manifest.data.project["requires-python"] = `~=${pythonVersion}.0`;
3566
+ }
3567
+ const content = (0, import_python_analysis.stringifyManifest)(manifest.data);
3568
+ pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
3569
+ await import_fs3.default.promises.writeFile(pyprojectPath, content);
3643
3570
  }
3644
- projectDir = (0, import_path4.dirname)(manifestPath);
3645
- pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
3646
- console.log(
3647
- "Installing required dependencies from requirements.txt with uv..."
3648
- );
3649
- if (!import_fs3.default.existsSync(pyprojectPath)) {
3650
- await createPyprojectToml({
3651
- projectName: "app",
3652
- pyprojectPath,
3653
- dependencies: [],
3654
- pythonVersion
3655
- });
3571
+ const workspaceLockFile = pythonPackage?.workspaceLockFile;
3572
+ if (workspaceLockFile) {
3573
+ lockPath = (0, import_path4.join)(rootDir, workspaceLockFile.path);
3574
+ } else {
3575
+ await uv.lock(projectDir);
3656
3576
  }
3657
- await uv.addFromFile({
3658
- venvPath,
3659
- projectDir,
3660
- requirementsPath: manifestPath
3661
- });
3662
3577
  } else {
3663
3578
  projectDir = workPath;
3664
3579
  pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
3665
3580
  console.log(
3666
3581
  "No Python manifest found; creating an empty pyproject.toml and uv.lock..."
3667
3582
  );
3668
- await createPyprojectToml({
3669
- projectName: "app",
3670
- pyprojectPath,
3671
- dependencies: [],
3672
- pythonVersion
3583
+ const requiresPython = `~=${pythonVersion}.0`;
3584
+ const minimalManifest = (0, import_python_analysis.createMinimalManifest)({
3585
+ name: "app",
3586
+ requiresPython,
3587
+ dependencies: []
3673
3588
  });
3589
+ const content = (0, import_python_analysis.stringifyManifest)(minimalManifest);
3590
+ await import_fs3.default.promises.writeFile(pyprojectPath, content);
3674
3591
  await uv.lock(projectDir);
3675
3592
  }
3676
- const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : findUvLockUpwards(projectDir, repoRootPath) || (0, import_path4.join)(projectDir, "uv.lock");
3593
+ const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : (0, import_path4.join)(projectDir, "uv.lock");
3677
3594
  return { projectDir, pyprojectPath, lockPath: resolvedLockPath };
3678
3595
  }
3679
3596
  async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
@@ -3767,46 +3684,6 @@ async function installRequirementsFile({
3767
3684
  targetDir
3768
3685
  );
3769
3686
  }
3770
- async function exportRequirementsFromPipfile({
3771
- pythonPath,
3772
- pipPath,
3773
- uvPath,
3774
- projectDir,
3775
- meta
3776
- }) {
3777
- const tempDir = await import_fs3.default.promises.mkdtemp(
3778
- (0, import_path4.join)(import_os2.default.tmpdir(), "vercel-pipenv-")
3779
- );
3780
- await installRequirement({
3781
- pythonPath,
3782
- pipPath,
3783
- dependency: "pipfile-requirements",
3784
- version: "0.3.0",
3785
- workPath: tempDir,
3786
- meta,
3787
- args: ["--no-warn-script-location"],
3788
- uvPath
3789
- });
3790
- const tempVendorDir = (0, import_path4.join)(tempDir, resolveVendorDir());
3791
- const convertCmd = isWin3 ? (0, import_path4.join)(tempVendorDir, "Scripts", "pipfile2req.exe") : (0, import_path4.join)(tempVendorDir, "bin", "pipfile2req");
3792
- (0, import_build_utils4.debug)(`Running "${convertCmd}" in ${projectDir}...`);
3793
- let stdout;
3794
- try {
3795
- const { stdout: out } = await (0, import_execa3.default)(convertCmd, [], {
3796
- cwd: projectDir,
3797
- env: { ...process.env, PYTHONPATH: tempVendorDir }
3798
- });
3799
- stdout = out;
3800
- } catch (err) {
3801
- throw new Error(
3802
- `Failed to run "${convertCmd}": ${err instanceof Error ? err.message : String(err)}`
3803
- );
3804
- }
3805
- const outPath = (0, import_path4.join)(tempDir, "requirements.pipenv.txt");
3806
- await import_fs3.default.promises.writeFile(outPath, stdout);
3807
- (0, import_build_utils4.debug)(`Exported pipfile requirements to ${outPath}`);
3808
- return outPath;
3809
- }
3810
3687
  async function mirrorSitePackagesIntoVendor({
3811
3688
  venvPath,
3812
3689
  vendorDirName
@@ -4044,6 +3921,21 @@ function createDevWsgiShim(workPath, modulePath) {
4044
3921
  return null;
4045
3922
  }
4046
3923
  }
3924
+ async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath) {
3925
+ const { pythonCmd, venvRoot } = useVirtualEnv(workPath, env, systemPython);
3926
+ if (venvRoot) {
3927
+ (0, import_build_utils7.debug)(`Using existing virtualenv at ${venvRoot} for multi-service dev`);
3928
+ return { command: pythonCmd, args: [] };
3929
+ }
3930
+ const venvPath = (0, import_path6.join)(workPath, ".venv");
3931
+ await ensureVenv({ pythonPath: systemPython, venvPath, uvPath });
3932
+ (0, import_build_utils7.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
3933
+ const pythonBin = getVenvPythonBin(venvPath);
3934
+ const binDir = getVenvBinDir(venvPath);
3935
+ env.VIRTUAL_ENV = venvPath;
3936
+ env.PATH = `${binDir}${import_path6.delimiter}${env.PATH || ""}`;
3937
+ return { command: pythonBin, args: [] };
3938
+ }
4047
3939
  var startDevServer = async (opts) => {
4048
3940
  const {
4049
3941
  entrypoint: rawEntrypoint,
@@ -4104,44 +3996,70 @@ var startDevServer = async (opts) => {
4104
3996
  let resolveChildReady;
4105
3997
  let rejectChildReady;
4106
3998
  const childReady = new Promise(
4107
- (resolve2, reject) => {
4108
- resolveChildReady = resolve2;
3999
+ (resolve, reject) => {
4000
+ resolveChildReady = resolve;
4109
4001
  rejectChildReady = reject;
4110
4002
  }
4111
4003
  );
4112
4004
  PENDING_STARTS.set(serverKey, childReady);
4113
4005
  try {
4114
- await new Promise((resolve2, reject) => {
4115
- let resolved = false;
4116
- const { pythonPath: systemPython } = getDefaultPythonVersion(meta);
4117
- let pythonCmd = systemPython;
4118
- const venv = isInVirtualEnv();
4119
- if (venv) {
4120
- (0, import_build_utils7.debug)(`Running in virtualenv at ${venv}`);
4006
+ const { pythonPath: systemPython } = getDefaultPythonVersion(meta);
4007
+ const uvPath = await findUvBinary(systemPython);
4008
+ const venv = isInVirtualEnv();
4009
+ const serviceCount = meta.serviceCount ?? 0;
4010
+ const pythonServiceCount = meta.pythonServiceCount ?? 1;
4011
+ if (venv && pythonServiceCount > 1) {
4012
+ const yellow = "\x1B[33m";
4013
+ const white = "\x1B[1m";
4014
+ const reset = "\x1B[0m";
4015
+ throw new import_build_utils7.NowBuildError({
4016
+ code: "PYTHON_EXTERNAL_VENV_DETECTED",
4017
+ message: `Detected activated venv at ${yellow}${venv}${reset}, ${white}vercel dev${reset} manages virtual environments automatically.
4018
+ Run ${white}deactivate${reset} and try again.`
4019
+ });
4020
+ }
4021
+ let spawnCommand = systemPython;
4022
+ let spawnArgsPrefix = [];
4023
+ if (serviceCount > 0) {
4024
+ const runner = await getMultiServicePythonRunner(
4025
+ workPath,
4026
+ env,
4027
+ systemPython,
4028
+ uvPath
4029
+ );
4030
+ spawnCommand = runner.command;
4031
+ spawnArgsPrefix = runner.args;
4032
+ (0, import_build_utils7.debug)(
4033
+ `Multi-service Python runner: ${spawnCommand} ${spawnArgsPrefix.join(" ")}`
4034
+ );
4035
+ } else if (venv) {
4036
+ (0, import_build_utils7.debug)(`Running in virtualenv at ${venv}`);
4037
+ } else {
4038
+ const { pythonCmd: venvPythonCmd, venvRoot } = useVirtualEnv(
4039
+ workPath,
4040
+ env,
4041
+ systemPython
4042
+ );
4043
+ spawnCommand = venvPythonCmd;
4044
+ if (venvRoot) {
4045
+ (0, import_build_utils7.debug)(`Using virtualenv at ${venvRoot}`);
4121
4046
  } else {
4122
- const { pythonCmd: venvPythonCmd, venvRoot } = useVirtualEnv(
4123
- workPath,
4124
- env,
4125
- systemPython
4126
- );
4127
- pythonCmd = venvPythonCmd;
4128
- if (venvRoot) {
4129
- (0, import_build_utils7.debug)(`Using virtualenv at ${venvRoot}`);
4130
- } else {
4131
- (0, import_build_utils7.debug)("No virtualenv found");
4132
- try {
4133
- const yellow = "\x1B[33m";
4134
- const reset = "\x1B[0m";
4135
- const venvCmd = process.platform === "win32" ? "python -m venv .venv && .venv\\Scripts\\activate" : "python -m venv .venv && source .venv/bin/activate";
4136
- process.stderr.write(
4137
- `${yellow}Warning: no virtual environment detected in ${workPath}. Using system Python: ${pythonCmd}.${reset}
4047
+ (0, import_build_utils7.debug)("No virtualenv found");
4048
+ try {
4049
+ const yellow = "\x1B[33m";
4050
+ const reset = "\x1B[0m";
4051
+ const venvCmd = process.platform === "win32" ? "python -m venv .venv && .venv\\Scripts\\activate" : "python -m venv .venv && source .venv/bin/activate";
4052
+ process.stderr.write(
4053
+ `${yellow}Warning: no virtual environment detected in ${workPath}. Using system Python: ${systemPython}.${reset}
4138
4054
  If you are using a virtual environment, activate it before running "vercel dev", or create one: ${venvCmd}
4139
4055
  `
4140
- );
4141
- } catch (_) {
4142
- }
4056
+ );
4057
+ } catch (_) {
4143
4058
  }
4144
4059
  }
4060
+ }
4061
+ await new Promise((resolve, reject) => {
4062
+ let resolved = false;
4145
4063
  if (framework !== "flask") {
4146
4064
  const devShimModule = createDevAsgiShim(workPath, modulePath);
4147
4065
  if (devShimModule) {
@@ -4150,11 +4068,12 @@ If you are using a virtual environment, activate it before running "vercel dev",
4150
4068
  env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
4151
4069
  }
4152
4070
  const moduleToRun = devShimModule || modulePath;
4153
- const argv = ["-u", "-m", moduleToRun];
4071
+ const pythonArgs = ["-u", "-m", moduleToRun];
4072
+ const argv = [...spawnArgsPrefix, ...pythonArgs];
4154
4073
  (0, import_build_utils7.debug)(
4155
- `Starting ASGI dev server (${framework}): ${pythonCmd} ${argv.join(" ")}`
4074
+ `Starting ASGI dev server (${framework}): ${spawnCommand} ${argv.join(" ")}`
4156
4075
  );
4157
- const child = (0, import_child_process2.spawn)(pythonCmd, argv, {
4076
+ const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
4158
4077
  cwd: workPath,
4159
4078
  env,
4160
4079
  stdio: ["inherit", "pipe", "pipe"]
@@ -4187,7 +4106,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
4187
4106
  child.stderr?.removeListener("data", onDetect);
4188
4107
  const port2 = Number(portMatch[1]);
4189
4108
  resolveChildReady({ port: port2, pid: child.pid });
4190
- resolve2();
4109
+ resolve();
4191
4110
  }
4192
4111
  }
4193
4112
  };
@@ -4216,9 +4135,10 @@ If you are using a virtual environment, activate it before running "vercel dev",
4216
4135
  env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
4217
4136
  }
4218
4137
  const moduleToRun = devShimModule || modulePath;
4219
- const argv = ["-u", "-m", moduleToRun];
4220
- (0, import_build_utils7.debug)(`Starting Flask dev server: ${pythonCmd} ${argv.join(" ")}`);
4221
- const child = (0, import_child_process2.spawn)(pythonCmd, argv, {
4138
+ const pythonArgs = ["-u", "-m", moduleToRun];
4139
+ const argv = [...spawnArgsPrefix, ...pythonArgs];
4140
+ (0, import_build_utils7.debug)(`Starting Flask dev server: ${spawnCommand} ${argv.join(" ")}`);
4141
+ const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
4222
4142
  cwd: workPath,
4223
4143
  env,
4224
4144
  stdio: ["inherit", "pipe", "pipe"]
@@ -4250,7 +4170,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
4250
4170
  child.stderr?.removeListener("data", onDetect);
4251
4171
  const port2 = Number(portMatch[1]);
4252
4172
  resolveChildReady({ port: port2, pid: child.pid });
4253
- resolve2();
4173
+ resolve();
4254
4174
  }
4255
4175
  }
4256
4176
  };
@@ -4522,14 +4442,9 @@ var build = async ({
4522
4442
  const { projectDir } = await ensureUvProject({
4523
4443
  workPath,
4524
4444
  entryDirectory,
4525
- fsFiles,
4526
4445
  repoRootPath,
4527
- pythonPath: pythonVersion.pythonPath,
4528
- pipPath: pythonVersion.pipPath,
4529
4446
  pythonVersion: pythonVersion.version,
4530
- uv,
4531
- venvPath,
4532
- meta
4447
+ uv
4533
4448
  });
4534
4449
  await uv.sync({
4535
4450
  venvPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.10.0",
3
+ "version": "6.11.1",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -15,6 +15,9 @@
15
15
  "url": "https://github.com/vercel/vercel.git",
16
16
  "directory": "packages/python"
17
17
  },
18
+ "dependencies": {
19
+ "@vercel/python-analysis": "0.4.0"
20
+ },
18
21
  "devDependencies": {
19
22
  "@renovatebot/pep440": "4.2.1",
20
23
  "@types/execa": "^0.9.0",
@@ -31,7 +34,7 @@
31
34
  "smol-toml": "1.5.2",
32
35
  "vitest": "2.1.4",
33
36
  "which": "3.0.0",
34
- "@vercel/build-utils": "13.3.4",
37
+ "@vercel/build-utils": "13.3.5",
35
38
  "@vercel/error-utils": "2.0.3"
36
39
  },
37
40
  "scripts": {