@vercel/python 6.29.0 → 6.30.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 +183 -64
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -423,15 +423,15 @@ var require_readShebang = __commonJS({
423
423
  var require_semver = __commonJS({
424
424
  "../../node_modules/.pnpm/semver@5.7.2/node_modules/semver/semver.js"(exports, module2) {
425
425
  exports = module2.exports = SemVer;
426
- var debug12;
426
+ var debug13;
427
427
  if (typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG)) {
428
- debug12 = function() {
428
+ debug13 = function() {
429
429
  var args = Array.prototype.slice.call(arguments, 0);
430
430
  args.unshift("SEMVER");
431
431
  console.log.apply(console, args);
432
432
  };
433
433
  } else {
434
- debug12 = function() {
434
+ debug13 = function() {
435
435
  };
436
436
  }
437
437
  exports.SEMVER_SPEC_VERSION = "2.0.0";
@@ -540,7 +540,7 @@ var require_semver = __commonJS({
540
540
  var STAR = R++;
541
541
  src[STAR] = "(<|>)?=?\\s*\\*";
542
542
  for (i = 0; i < R; i++) {
543
- debug12(i, src[i]);
543
+ debug13(i, src[i]);
544
544
  if (!re[i]) {
545
545
  re[i] = new RegExp(src[i]);
546
546
  safeRe[i] = new RegExp(makeSafeRe(src[i]));
@@ -607,7 +607,7 @@ var require_semver = __commonJS({
607
607
  if (!(this instanceof SemVer)) {
608
608
  return new SemVer(version2, options);
609
609
  }
610
- debug12("SemVer", version2, options);
610
+ debug13("SemVer", version2, options);
611
611
  this.options = options;
612
612
  this.loose = !!options.loose;
613
613
  var m = version2.trim().match(options.loose ? safeRe[LOOSE] : safeRe[FULL]);
@@ -654,7 +654,7 @@ var require_semver = __commonJS({
654
654
  return this.version;
655
655
  };
656
656
  SemVer.prototype.compare = function(other) {
657
- debug12("SemVer.compare", this.version, this.options, other);
657
+ debug13("SemVer.compare", this.version, this.options, other);
658
658
  if (!(other instanceof SemVer)) {
659
659
  other = new SemVer(other, this.options);
660
660
  }
@@ -681,7 +681,7 @@ var require_semver = __commonJS({
681
681
  do {
682
682
  var a = this.prerelease[i2];
683
683
  var b = other.prerelease[i2];
684
- debug12("prerelease compare", i2, a, b);
684
+ debug13("prerelease compare", i2, a, b);
685
685
  if (a === void 0 && b === void 0) {
686
686
  return 0;
687
687
  } else if (b === void 0) {
@@ -935,7 +935,7 @@ var require_semver = __commonJS({
935
935
  return new Comparator(comp, options);
936
936
  }
937
937
  comp = comp.trim().split(/\s+/).join(" ");
938
- debug12("comparator", comp, options);
938
+ debug13("comparator", comp, options);
939
939
  this.options = options;
940
940
  this.loose = !!options.loose;
941
941
  this.parse(comp);
@@ -944,7 +944,7 @@ var require_semver = __commonJS({
944
944
  } else {
945
945
  this.value = this.operator + this.semver.version;
946
946
  }
947
- debug12("comp", this);
947
+ debug13("comp", this);
948
948
  }
949
949
  var ANY = {};
950
950
  Comparator.prototype.parse = function(comp) {
@@ -967,7 +967,7 @@ var require_semver = __commonJS({
967
967
  return this.value;
968
968
  };
969
969
  Comparator.prototype.test = function(version2) {
970
- debug12("Comparator.test", version2, this.options.loose);
970
+ debug13("Comparator.test", version2, this.options.loose);
971
971
  if (this.semver === ANY) {
972
972
  return true;
973
973
  }
@@ -1050,9 +1050,9 @@ var require_semver = __commonJS({
1050
1050
  var loose = this.options.loose;
1051
1051
  var hr = loose ? safeRe[HYPHENRANGELOOSE] : safeRe[HYPHENRANGE];
1052
1052
  range = range.replace(hr, hyphenReplace);
1053
- debug12("hyphen replace", range);
1053
+ debug13("hyphen replace", range);
1054
1054
  range = range.replace(safeRe[COMPARATORTRIM], comparatorTrimReplace);
1055
- debug12("comparator trim", range, safeRe[COMPARATORTRIM]);
1055
+ debug13("comparator trim", range, safeRe[COMPARATORTRIM]);
1056
1056
  range = range.replace(safeRe[TILDETRIM], tildeTrimReplace);
1057
1057
  range = range.replace(safeRe[CARETTRIM], caretTrimReplace);
1058
1058
  var compRe = loose ? safeRe[COMPARATORLOOSE] : safeRe[COMPARATOR];
@@ -1092,15 +1092,15 @@ var require_semver = __commonJS({
1092
1092
  });
1093
1093
  }
1094
1094
  function parseComparator(comp, options) {
1095
- debug12("comp", comp, options);
1095
+ debug13("comp", comp, options);
1096
1096
  comp = replaceCarets(comp, options);
1097
- debug12("caret", comp);
1097
+ debug13("caret", comp);
1098
1098
  comp = replaceTildes(comp, options);
1099
- debug12("tildes", comp);
1099
+ debug13("tildes", comp);
1100
1100
  comp = replaceXRanges(comp, options);
1101
- debug12("xrange", comp);
1101
+ debug13("xrange", comp);
1102
1102
  comp = replaceStars(comp, options);
1103
- debug12("stars", comp);
1103
+ debug13("stars", comp);
1104
1104
  return comp;
1105
1105
  }
1106
1106
  function isX(id) {
@@ -1114,7 +1114,7 @@ var require_semver = __commonJS({
1114
1114
  function replaceTilde(comp, options) {
1115
1115
  var r = options.loose ? safeRe[TILDELOOSE] : safeRe[TILDE];
1116
1116
  return comp.replace(r, function(_, M, m, p, pr) {
1117
- debug12("tilde", comp, _, M, m, p, pr);
1117
+ debug13("tilde", comp, _, M, m, p, pr);
1118
1118
  var ret;
1119
1119
  if (isX(M)) {
1120
1120
  ret = "";
@@ -1123,12 +1123,12 @@ var require_semver = __commonJS({
1123
1123
  } else if (isX(p)) {
1124
1124
  ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0";
1125
1125
  } else if (pr) {
1126
- debug12("replaceTilde pr", pr);
1126
+ debug13("replaceTilde pr", pr);
1127
1127
  ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + M + "." + (+m + 1) + ".0";
1128
1128
  } else {
1129
1129
  ret = ">=" + M + "." + m + "." + p + " <" + M + "." + (+m + 1) + ".0";
1130
1130
  }
1131
- debug12("tilde return", ret);
1131
+ debug13("tilde return", ret);
1132
1132
  return ret;
1133
1133
  });
1134
1134
  }
@@ -1138,10 +1138,10 @@ var require_semver = __commonJS({
1138
1138
  }).join(" ");
1139
1139
  }
1140
1140
  function replaceCaret(comp, options) {
1141
- debug12("caret", comp, options);
1141
+ debug13("caret", comp, options);
1142
1142
  var r = options.loose ? safeRe[CARETLOOSE] : safeRe[CARET];
1143
1143
  return comp.replace(r, function(_, M, m, p, pr) {
1144
- debug12("caret", comp, _, M, m, p, pr);
1144
+ debug13("caret", comp, _, M, m, p, pr);
1145
1145
  var ret;
1146
1146
  if (isX(M)) {
1147
1147
  ret = "";
@@ -1154,7 +1154,7 @@ var require_semver = __commonJS({
1154
1154
  ret = ">=" + M + "." + m + ".0 <" + (+M + 1) + ".0.0";
1155
1155
  }
1156
1156
  } else if (pr) {
1157
- debug12("replaceCaret pr", pr);
1157
+ debug13("replaceCaret pr", pr);
1158
1158
  if (M === "0") {
1159
1159
  if (m === "0") {
1160
1160
  ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + M + "." + m + "." + (+p + 1);
@@ -1165,7 +1165,7 @@ var require_semver = __commonJS({
1165
1165
  ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + (+M + 1) + ".0.0";
1166
1166
  }
1167
1167
  } else {
1168
- debug12("no pr");
1168
+ debug13("no pr");
1169
1169
  if (M === "0") {
1170
1170
  if (m === "0") {
1171
1171
  ret = ">=" + M + "." + m + "." + p + " <" + M + "." + m + "." + (+p + 1);
@@ -1176,12 +1176,12 @@ var require_semver = __commonJS({
1176
1176
  ret = ">=" + M + "." + m + "." + p + " <" + (+M + 1) + ".0.0";
1177
1177
  }
1178
1178
  }
1179
- debug12("caret return", ret);
1179
+ debug13("caret return", ret);
1180
1180
  return ret;
1181
1181
  });
1182
1182
  }
1183
1183
  function replaceXRanges(comp, options) {
1184
- debug12("replaceXRanges", comp, options);
1184
+ debug13("replaceXRanges", comp, options);
1185
1185
  return comp.split(/\s+/).map(function(comp2) {
1186
1186
  return replaceXRange(comp2, options);
1187
1187
  }).join(" ");
@@ -1190,7 +1190,7 @@ var require_semver = __commonJS({
1190
1190
  comp = comp.trim();
1191
1191
  var r = options.loose ? safeRe[XRANGELOOSE] : safeRe[XRANGE];
1192
1192
  return comp.replace(r, function(ret, gtlt, M, m, p, pr) {
1193
- debug12("xRange", comp, ret, gtlt, M, m, p, pr);
1193
+ debug13("xRange", comp, ret, gtlt, M, m, p, pr);
1194
1194
  var xM = isX(M);
1195
1195
  var xm = xM || isX(m);
1196
1196
  var xp = xm || isX(p);
@@ -1233,12 +1233,12 @@ var require_semver = __commonJS({
1233
1233
  } else if (xp) {
1234
1234
  ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0";
1235
1235
  }
1236
- debug12("xRange return", ret);
1236
+ debug13("xRange return", ret);
1237
1237
  return ret;
1238
1238
  });
1239
1239
  }
1240
1240
  function replaceStars(comp, options) {
1241
- debug12("replaceStars", comp, options);
1241
+ debug13("replaceStars", comp, options);
1242
1242
  return comp.trim().replace(safeRe[STAR], "");
1243
1243
  }
1244
1244
  function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) {
@@ -1286,7 +1286,7 @@ var require_semver = __commonJS({
1286
1286
  }
1287
1287
  if (version2.prerelease.length && !options.includePrerelease) {
1288
1288
  for (i2 = 0; i2 < set.length; i2++) {
1289
- debug12(set[i2].semver);
1289
+ debug13(set[i2].semver);
1290
1290
  if (set[i2].semver === ANY) {
1291
1291
  continue;
1292
1292
  }
@@ -3217,6 +3217,7 @@ __export(src_exports, {
3217
3217
  downloadFilesInWorkPath: () => downloadFilesInWorkPath,
3218
3218
  installRequirement: () => installRequirement,
3219
3219
  installRequirementsFile: () => installRequirementsFile,
3220
+ prepareCache: () => prepareCache,
3220
3221
  runFrameworkHook: () => runFrameworkHook,
3221
3222
  shouldServe: () => shouldServe,
3222
3223
  startDevServer: () => startDevServer,
@@ -3229,7 +3230,7 @@ var import_path13 = require("path");
3229
3230
 
3230
3231
  // src/package-versions.ts
3231
3232
  var VERCEL_RUNTIME_VERSION = "0.12.0";
3232
- var VERCEL_WORKERS_VERSION = "0.0.13";
3233
+ var VERCEL_WORKERS_VERSION = "0.0.15";
3233
3234
 
3234
3235
  // src/index.ts
3235
3236
  var import_build_utils15 = require("@vercel/build-utils");
@@ -3261,14 +3262,19 @@ var import_build_utils = require("@vercel/build-utils");
3261
3262
  var UV_VERSION = "0.10.11";
3262
3263
  var UV_PYTHON_PATH_PREFIX = "/uv/python/";
3263
3264
  var UV_PYTHON_DOWNLOADS_MODE = "automatic";
3265
+ var UV_CACHE_DIR_SUBPATH = [".vercel", "python", "cache", "uv"];
3264
3266
  var isWin = process.platform === "win32";
3265
3267
  var uvExec = isWin ? "uv.exe" : "uv";
3266
3268
  function findUvInPath() {
3267
3269
  return import_which.default.sync("uv", { nothrow: true });
3268
3270
  }
3271
+ function getUvCacheDir(workPath) {
3272
+ return (0, import_path.join)(workPath, ...UV_CACHE_DIR_SUBPATH);
3273
+ }
3269
3274
  var UvRunner = class {
3270
- constructor(uvPath) {
3275
+ constructor(uvPath, uvCacheDir) {
3271
3276
  this.uvPath = uvPath;
3277
+ this.uvCacheDir = uvCacheDir;
3272
3278
  }
3273
3279
  getPath() {
3274
3280
  return this.uvPath;
@@ -3317,7 +3323,7 @@ var UvRunner = class {
3317
3323
  }
3318
3324
  async sync(options) {
3319
3325
  const { venvPath, projectDir, locked, frozen, noBuild, noInstallProject } = options;
3320
- const args = ["sync", "--active", "--no-dev", "--link-mode", "copy"];
3326
+ const args = ["sync", "--active", "--no-dev", "--link-mode", "hardlink"];
3321
3327
  if (frozen) {
3322
3328
  args.push("--frozen");
3323
3329
  } else if (locked) {
@@ -3366,6 +3372,24 @@ var UvRunner = class {
3366
3372
  const fullArgs = ["pip", ...args];
3367
3373
  await this.runUvCmd(fullArgs, projectDir, venvPath);
3368
3374
  }
3375
+ /**
3376
+ * Prune the uv cache for CI: removes pre-built wheels and unzipped source
3377
+ * distributions while retaining source-built wheels.
3378
+ */
3379
+ async cachePrune() {
3380
+ const args = ["cache", "prune", "--ci"];
3381
+ const pretty = `uv ${args.join(" ")}`;
3382
+ (0, import_build_utils.debug)(`Running "${pretty}"...`);
3383
+ try {
3384
+ await (0, import_execa.default)(this.uvPath, args, {
3385
+ env: getProtectedUvEnv(process.env, this.uvCacheDir)
3386
+ });
3387
+ } catch (err) {
3388
+ (0, import_build_utils.debug)(
3389
+ `Warning: ${pretty} failed: ${err instanceof Error ? err.message : String(err)}`
3390
+ );
3391
+ }
3392
+ }
3369
3393
  async runUvCmd(args, cwd, venvPath) {
3370
3394
  const pretty = `uv ${args.join(" ")}`;
3371
3395
  (0, import_build_utils.debug)(`Running "${pretty}"...`);
@@ -3392,7 +3416,7 @@ var UvRunner = class {
3392
3416
  const binDir = isWin ? (0, import_path.join)(venvPath, "Scripts") : (0, import_path.join)(venvPath, "bin");
3393
3417
  const existingPath = process.env.PATH || "";
3394
3418
  return {
3395
- ...getProtectedUvEnv(process.env),
3419
+ ...getProtectedUvEnv(process.env, this.uvCacheDir),
3396
3420
  VIRTUAL_ENV: venvPath,
3397
3421
  PATH: existingPath ? `${binDir}${import_path2.delimiter}${existingPath}` : binDir
3398
3422
  };
@@ -3499,11 +3523,15 @@ async function getUvBinaryOrInstall(pythonPath) {
3499
3523
  function filterUnsafeUvPipArgs(args) {
3500
3524
  return args.filter((arg) => arg !== "--no-warn-script-location");
3501
3525
  }
3502
- function getProtectedUvEnv(baseEnv = process.env) {
3503
- return {
3526
+ function getProtectedUvEnv(baseEnv = process.env, uvCacheDir) {
3527
+ const env = {
3504
3528
  ...baseEnv,
3505
3529
  UV_PYTHON_DOWNLOADS: UV_PYTHON_DOWNLOADS_MODE
3506
3530
  };
3531
+ if (uvCacheDir) {
3532
+ env.UV_CACHE_DIR = uvCacheDir;
3533
+ }
3534
+ return env;
3507
3535
  }
3508
3536
  var UV_BUNDLE_DIR = "_uv";
3509
3537
  async function getUvBinaryForBundling(pythonPath) {
@@ -3542,9 +3570,9 @@ function useVirtualEnv(workPath, env, systemPython) {
3542
3570
  }
3543
3571
  return { pythonCmd };
3544
3572
  }
3545
- function createVenvEnv(venvPath, baseEnv = process.env) {
3573
+ function createVenvEnv(venvPath, baseEnv = process.env, uvCacheDir) {
3546
3574
  const env = {
3547
- ...getProtectedUvEnv(baseEnv),
3575
+ ...getProtectedUvEnv(baseEnv, uvCacheDir),
3548
3576
  VIRTUAL_ENV: venvPath
3549
3577
  };
3550
3578
  const binDir = getVenvBinDir(venvPath);
@@ -3552,26 +3580,60 @@ function createVenvEnv(venvPath, baseEnv = process.env) {
3552
3580
  env.PATH = existingPath ? `${binDir}${import_path3.delimiter}${existingPath}` : binDir;
3553
3581
  return env;
3554
3582
  }
3583
+ async function readVenvPythonVersion(pyvenvCfgPath) {
3584
+ try {
3585
+ const content = await import_fs2.default.promises.readFile(pyvenvCfgPath, "utf-8");
3586
+ const match = content.match(/^version\s*=\s*(\d+)\.(\d+)/m);
3587
+ return match ? `${match[1]}.${match[2]}` : null;
3588
+ } catch {
3589
+ return null;
3590
+ }
3591
+ }
3555
3592
  async function ensureVenv({
3556
- pythonPath,
3593
+ pythonVersion,
3557
3594
  venvPath,
3558
3595
  uvPath,
3596
+ uvCacheDir,
3559
3597
  quiet
3560
3598
  }) {
3561
3599
  const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
3600
+ let venvExists = false;
3562
3601
  try {
3563
3602
  await import_fs2.default.promises.access(marker);
3564
- return;
3603
+ venvExists = true;
3565
3604
  } catch {
3566
3605
  }
3567
- await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
3568
- if (!quiet) {
3569
- console.log(`Creating virtual environment at "${venvPath}"...`);
3606
+ if (venvExists && pythonVersion.major != null && pythonVersion.minor != null) {
3607
+ const expected = `${pythonVersion.major}.${pythonVersion.minor}`;
3608
+ const cachedVersion = await readVenvPythonVersion(marker);
3609
+ if (cachedVersion && cachedVersion !== expected) {
3610
+ if (!quiet) {
3611
+ console.log(
3612
+ `Cached venv Python ${cachedVersion} differs from requested ${expected}, recreating...`
3613
+ );
3614
+ }
3615
+ await import_fs2.default.promises.rm(venvPath, { recursive: true, force: true });
3616
+ venvExists = false;
3617
+ }
3618
+ }
3619
+ if (venvExists) {
3620
+ (0, import_build_utils2.debug)(`Refreshing cached virtual environment at "${venvPath}"`);
3621
+ } else {
3622
+ await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
3623
+ if (!quiet) {
3624
+ console.log(`Creating virtual environment at "${venvPath}"...`);
3625
+ }
3570
3626
  }
3571
3627
  if (uvPath) {
3572
- await (0, import_execa2.default)(uvPath, ["venv", venvPath]);
3628
+ const args = ["venv", venvPath, "--allow-existing"];
3629
+ if (pythonVersion.major != null && pythonVersion.minor != null) {
3630
+ args.push("--python", `${pythonVersion.major}.${pythonVersion.minor}`);
3631
+ }
3632
+ await (0, import_execa2.default)(uvPath, args, {
3633
+ env: getProtectedUvEnv(process.env, uvCacheDir)
3634
+ });
3573
3635
  } else {
3574
- await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
3636
+ await (0, import_execa2.default)(pythonVersion.pythonPath, ["-m", "venv", venvPath]);
3575
3637
  }
3576
3638
  }
3577
3639
  function getVenvPythonBin(venvPath) {
@@ -3900,6 +3962,15 @@ function isInstalled(pv) {
3900
3962
  }
3901
3963
 
3902
3964
  // src/install.ts
3965
+ async function restoreCachedLock(cachedLockPath, projectDir) {
3966
+ if (!cachedLockPath)
3967
+ return;
3968
+ const targetLockPath = (0, import_path5.join)(projectDir, "uv.lock");
3969
+ if (import_fs3.default.existsSync(cachedLockPath) && !import_fs3.default.existsSync(targetLockPath)) {
3970
+ (0, import_build_utils4.debug)("Restoring cached uv.lock");
3971
+ await import_fs3.default.promises.copyFile(cachedLockPath, targetLockPath);
3972
+ }
3973
+ }
3903
3974
  var makeDependencyCheckCode = (dependency) => `
3904
3975
  from importlib import util
3905
3976
  dep = '${dependency}'.replace('-', '_')
@@ -4024,8 +4095,8 @@ async function ensureUvProject({
4024
4095
  pythonPackage,
4025
4096
  pythonVersion,
4026
4097
  uv,
4027
- generateLockFile = false,
4028
- requireBinaryWheels = false
4098
+ requireBinaryWheels = false,
4099
+ cachedLockPath
4029
4100
  }) {
4030
4101
  const { manifestType } = detectInstallSource(pythonPackage, rootDir);
4031
4102
  const manifest = pythonPackage.manifest;
@@ -4077,18 +4148,13 @@ async function ensureUvProject({
4077
4148
  if (workspaceLockFile) {
4078
4149
  lockPath = (0, import_path5.join)(rootDir, workspaceLockFile.path);
4079
4150
  } else {
4151
+ await restoreCachedLock(cachedLockPath, projectDir);
4080
4152
  await uv.lock({
4081
4153
  projectDir,
4082
4154
  venvPath,
4083
4155
  ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4084
4156
  });
4085
- }
4086
- if (generateLockFile && !lockPath) {
4087
- await uv.lock({
4088
- projectDir,
4089
- venvPath,
4090
- ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4091
- });
4157
+ lockPath = (0, import_path5.join)(projectDir, "uv.lock");
4092
4158
  }
4093
4159
  } else {
4094
4160
  projectDir = workPath;
@@ -4104,6 +4170,7 @@ async function ensureUvProject({
4104
4170
  });
4105
4171
  const content = (0, import_python_analysis2.stringifyManifest)(minimalManifest);
4106
4172
  await import_fs3.default.promises.writeFile(pyprojectPath, content);
4173
+ await restoreCachedLock(cachedLockPath, projectDir);
4107
4174
  await uv.lock({
4108
4175
  projectDir,
4109
4176
  venvPath,
@@ -4136,7 +4203,7 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
4136
4203
  try {
4137
4204
  await (0, import_execa3.default)(uvPath, uvArgs, {
4138
4205
  cwd: workPath,
4139
- env: getProtectedUvEnv()
4206
+ env: getProtectedUvEnv(process.env, getUvCacheDir(workPath))
4140
4207
  });
4141
4208
  return;
4142
4209
  } catch (err) {
@@ -5664,7 +5731,12 @@ async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath)
5664
5731
  return { command: pythonCmd, args: [] };
5665
5732
  }
5666
5733
  const venvPath = (0, import_path9.join)(workPath, ".venv");
5667
- await ensureVenv({ pythonPath: systemPython, venvPath, uvPath, quiet: true });
5734
+ await ensureVenv({
5735
+ pythonVersion: { pythonPath: systemPython },
5736
+ venvPath,
5737
+ uvPath,
5738
+ quiet: true
5739
+ });
5668
5740
  (0, import_build_utils10.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
5669
5741
  const pythonBin = getVenvPythonBin(venvPath);
5670
5742
  const binDir = getVenvBinDir(venvPath);
@@ -6676,10 +6748,19 @@ var build = async ({
6676
6748
  );
6677
6749
  }
6678
6750
  const venvPath = service?.name ? (0, import_path13.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path13.join)(workPath, ".vercel", "python", ".venv");
6751
+ const uvCacheDir = getUvCacheDir(workPath);
6752
+ const hasCachedVenv = import_fs11.default.existsSync((0, import_path13.join)(venvPath, "pyvenv.cfg"));
6753
+ const hasCachedUv = import_fs11.default.existsSync(uvCacheDir);
6754
+ if (hasCachedVenv || hasCachedUv) {
6755
+ (0, import_build_utils15.debug)(
6756
+ `Build cache detected: venv=${hasCachedVenv}, uv-cache=${hasCachedUv}`
6757
+ );
6758
+ }
6679
6759
  await builderSpan.child("vc.builder.python.venv").trace(async () => {
6680
6760
  await ensureVenv({
6681
- pythonPath: pythonVersion.pythonPath,
6682
- venvPath
6761
+ pythonVersion,
6762
+ venvPath,
6763
+ uvCacheDir
6683
6764
  });
6684
6765
  });
6685
6766
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -6708,13 +6789,13 @@ var build = async ({
6708
6789
  }
6709
6790
  }
6710
6791
  const baseEnv = spawnEnv || process.env;
6711
- const pythonEnv = createVenvEnv(venvPath, baseEnv);
6792
+ const pythonEnv = createVenvEnv(venvPath, baseEnv, uvCacheDir);
6712
6793
  pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
6713
6794
  let assumeDepsInstalled = false;
6714
6795
  let uv;
6715
6796
  try {
6716
6797
  const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
6717
- uv = new UvRunner(uvPath);
6798
+ uv = new UvRunner(uvPath, uvCacheDir);
6718
6799
  const uvVersionOutput = (0, import_child_process3.execSync)(`${uvPath} --version`, {
6719
6800
  encoding: "utf8"
6720
6801
  }).trim();
@@ -6732,6 +6813,13 @@ var build = async ({
6732
6813
  installCommand: projectInstallCommand || void 0
6733
6814
  }).trace(async () => {
6734
6815
  if (projectInstallCommand) {
6816
+ await import_fs11.default.promises.rm(venvPath, { recursive: true, force: true });
6817
+ await ensureVenv({
6818
+ pythonVersion,
6819
+ venvPath,
6820
+ uvCacheDir,
6821
+ quiet: true
6822
+ });
6735
6823
  console.log(
6736
6824
  `Running "install" command: \`${projectInstallCommand}\`...`
6737
6825
  );
@@ -6742,18 +6830,21 @@ var build = async ({
6742
6830
  assumeDepsInstalled = true;
6743
6831
  hasCustomCommand = true;
6744
6832
  } else {
6745
- assumeDepsInstalled = await runPyprojectScript(
6833
+ const hasCustomScript = await runPyprojectScript(
6746
6834
  workPath,
6747
6835
  ["vercel-install", "now-install", "install"],
6748
6836
  pythonEnv,
6749
6837
  /* useUserVirtualEnv */
6750
6838
  false
6751
6839
  );
6752
- if (assumeDepsInstalled) {
6840
+ if (hasCustomScript) {
6841
+ assumeDepsInstalled = true;
6753
6842
  hasCustomCommand = true;
6754
6843
  }
6755
6844
  }
6756
6845
  if (!assumeDepsInstalled) {
6846
+ const lockCacheKey = service?.name ? `uv.lock.${service.name}` : "uv.lock";
6847
+ const cachedLockPath = (0, import_path13.join)(uvCacheDir, lockCacheKey);
6757
6848
  const { projectDir, lockPath, lockFileProvidedByUser } = await ensureUvProject({
6758
6849
  workPath,
6759
6850
  rootDir,
@@ -6761,8 +6852,8 @@ var build = async ({
6761
6852
  pythonPackage,
6762
6853
  pythonVersion: pythonVersionString(pythonVersion),
6763
6854
  uv,
6764
- generateLockFile: true,
6765
- requireBinaryWheels: false
6855
+ requireBinaryWheels: false,
6856
+ cachedLockPath
6766
6857
  });
6767
6858
  uvLockPath = lockPath;
6768
6859
  uvProjectDir = projectDir;
@@ -6773,6 +6864,10 @@ var build = async ({
6773
6864
  frozen: lockFileProvidedByUser,
6774
6865
  locked: !lockFileProvidedByUser
6775
6866
  });
6867
+ if (lockPath && import_fs11.default.existsSync(lockPath)) {
6868
+ await import_fs11.default.promises.mkdir(uvCacheDir, { recursive: true });
6869
+ await import_fs11.default.promises.copyFile(lockPath, cachedLockPath);
6870
+ }
6776
6871
  }
6777
6872
  });
6778
6873
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -7008,6 +7103,29 @@ from vercel_runtime.vc_init import vc_handler
7008
7103
  }
7009
7104
  };
7010
7105
  };
7106
+ var prepareCache = async ({
7107
+ repoRootPath,
7108
+ workPath
7109
+ }) => {
7110
+ if (process.env.VERCEL_PYTHON_PREPARE_CACHE !== "1") {
7111
+ return {};
7112
+ }
7113
+ const root = repoRootPath || workPath;
7114
+ const ignore = ["**/*.pyc", "**/__pycache__/**"];
7115
+ const uvCacheDir = getUvCacheDir(workPath);
7116
+ try {
7117
+ const uvPath = findUvInPath();
7118
+ if (uvPath) {
7119
+ const uv = new UvRunner(uvPath, uvCacheDir);
7120
+ await uv.cachePrune();
7121
+ }
7122
+ } catch {
7123
+ }
7124
+ return (0, import_build_utils15.glob)("**/.vercel/python/{.venv,services/*/.venv,cache/uv}/**", {
7125
+ cwd: root,
7126
+ ignore
7127
+ });
7128
+ };
7011
7129
  var shouldServe = (opts) => {
7012
7130
  const framework = opts.config.framework;
7013
7131
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -7046,6 +7164,7 @@ function hasProp(obj, key) {
7046
7164
  downloadFilesInWorkPath,
7047
7165
  installRequirement,
7048
7166
  installRequirementsFile,
7167
+ prepareCache,
7049
7168
  runFrameworkHook,
7050
7169
  shouldServe,
7051
7170
  startDevServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.29.0",
3
+ "version": "6.30.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -36,9 +36,9 @@
36
36
  "smol-toml": "1.5.2",
37
37
  "vitest": "2.1.4",
38
38
  "which": "3.0.0",
39
- "@vercel/build-utils": "13.12.1",
40
39
  "@vercel/error-utils": "2.0.3",
41
- "@vercel/python-runtime": "0.12.0"
40
+ "@vercel/python-runtime": "0.12.0",
41
+ "@vercel/build-utils": "13.14.2"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "node ../../utils/build-builder.mjs",