@vercel/python 6.29.0 → 6.30.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 +277 -134
  2. package/package.json +2 -2
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,
@@ -3224,19 +3225,19 @@ __export(src_exports, {
3224
3225
  });
3225
3226
  module.exports = __toCommonJS(src_exports);
3226
3227
  var import_child_process3 = require("child_process");
3227
- var import_fs11 = __toESM(require("fs"));
3228
+ var import_fs12 = __toESM(require("fs"));
3228
3229
  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.16";
3233
3234
 
3234
3235
  // src/index.ts
3235
3236
  var import_build_utils15 = require("@vercel/build-utils");
3236
3237
 
3237
3238
  // src/install.ts
3238
3239
  var import_execa3 = __toESM(require_execa());
3239
- var import_fs3 = __toESM(require("fs"));
3240
+ var import_fs4 = __toESM(require("fs"));
3240
3241
  var import_path5 = require("path");
3241
3242
  var import_build_utils4 = require("@vercel/build-utils");
3242
3243
  var import_python_analysis2 = require("@vercel/python-analysis");
@@ -3261,14 +3262,25 @@ 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";
3268
+ var KNOWN_UV_PATH = "/usr/local/bin/uv";
3269
+ function findUvOnBuildImage(knownPath = KNOWN_UV_PATH) {
3270
+ if (!process.env.VERCEL_BUILD_IMAGE)
3271
+ return null;
3272
+ return import_fs.default.existsSync(knownPath) ? knownPath : null;
3273
+ }
3266
3274
  function findUvInPath() {
3267
- return import_which.default.sync("uv", { nothrow: true });
3275
+ return findUvOnBuildImage() ?? import_which.default.sync("uv", { nothrow: true });
3276
+ }
3277
+ function getUvCacheDir(workPath) {
3278
+ return (0, import_path.join)(workPath, ...UV_CACHE_DIR_SUBPATH);
3268
3279
  }
3269
3280
  var UvRunner = class {
3270
- constructor(uvPath) {
3281
+ constructor(uvPath, uvCacheDir) {
3271
3282
  this.uvPath = uvPath;
3283
+ this.uvCacheDir = uvCacheDir;
3272
3284
  }
3273
3285
  getPath() {
3274
3286
  return this.uvPath;
@@ -3317,7 +3329,7 @@ var UvRunner = class {
3317
3329
  }
3318
3330
  async sync(options) {
3319
3331
  const { venvPath, projectDir, locked, frozen, noBuild, noInstallProject } = options;
3320
- const args = ["sync", "--active", "--no-dev", "--link-mode", "copy"];
3332
+ const args = ["sync", "--active", "--no-dev", "--link-mode", "hardlink"];
3321
3333
  if (frozen) {
3322
3334
  args.push("--frozen");
3323
3335
  } else if (locked) {
@@ -3366,6 +3378,24 @@ var UvRunner = class {
3366
3378
  const fullArgs = ["pip", ...args];
3367
3379
  await this.runUvCmd(fullArgs, projectDir, venvPath);
3368
3380
  }
3381
+ /**
3382
+ * Prune the uv cache for CI: removes pre-built wheels and unzipped source
3383
+ * distributions while retaining source-built wheels.
3384
+ */
3385
+ async cachePrune() {
3386
+ const args = ["cache", "prune", "--ci"];
3387
+ const pretty = `uv ${args.join(" ")}`;
3388
+ (0, import_build_utils.debug)(`Running "${pretty}"...`);
3389
+ try {
3390
+ await (0, import_execa.default)(this.uvPath, args, {
3391
+ env: getProtectedUvEnv(process.env, this.uvCacheDir)
3392
+ });
3393
+ } catch (err) {
3394
+ (0, import_build_utils.debug)(
3395
+ `Warning: ${pretty} failed: ${err instanceof Error ? err.message : String(err)}`
3396
+ );
3397
+ }
3398
+ }
3369
3399
  async runUvCmd(args, cwd, venvPath) {
3370
3400
  const pretty = `uv ${args.join(" ")}`;
3371
3401
  (0, import_build_utils.debug)(`Running "${pretty}"...`);
@@ -3392,7 +3422,7 @@ var UvRunner = class {
3392
3422
  const binDir = isWin ? (0, import_path.join)(venvPath, "Scripts") : (0, import_path.join)(venvPath, "bin");
3393
3423
  const existingPath = process.env.PATH || "";
3394
3424
  return {
3395
- ...getProtectedUvEnv(process.env),
3425
+ ...getProtectedUvEnv(process.env, this.uvCacheDir),
3396
3426
  VIRTUAL_ENV: venvPath,
3397
3427
  PATH: existingPath ? `${binDir}${import_path2.delimiter}${existingPath}` : binDir
3398
3428
  };
@@ -3424,6 +3454,9 @@ async function getUserScriptsDir(pythonPath) {
3424
3454
  }
3425
3455
  }
3426
3456
  async function findUvBinary(pythonPath) {
3457
+ const buildImageUv = findUvOnBuildImage();
3458
+ if (buildImageUv)
3459
+ return buildImageUv;
3427
3460
  const found = import_which.default.sync("uv", { nothrow: true });
3428
3461
  if (found)
3429
3462
  return found;
@@ -3499,11 +3532,15 @@ async function getUvBinaryOrInstall(pythonPath) {
3499
3532
  function filterUnsafeUvPipArgs(args) {
3500
3533
  return args.filter((arg) => arg !== "--no-warn-script-location");
3501
3534
  }
3502
- function getProtectedUvEnv(baseEnv = process.env) {
3503
- return {
3535
+ function getProtectedUvEnv(baseEnv = process.env, uvCacheDir) {
3536
+ const env = {
3504
3537
  ...baseEnv,
3505
3538
  UV_PYTHON_DOWNLOADS: UV_PYTHON_DOWNLOADS_MODE
3506
3539
  };
3540
+ if (uvCacheDir) {
3541
+ env.UV_CACHE_DIR = uvCacheDir;
3542
+ }
3543
+ return env;
3507
3544
  }
3508
3545
  var UV_BUNDLE_DIR = "_uv";
3509
3546
  async function getUvBinaryForBundling(pythonPath) {
@@ -3542,9 +3579,9 @@ function useVirtualEnv(workPath, env, systemPython) {
3542
3579
  }
3543
3580
  return { pythonCmd };
3544
3581
  }
3545
- function createVenvEnv(venvPath, baseEnv = process.env) {
3582
+ function createVenvEnv(venvPath, baseEnv = process.env, uvCacheDir) {
3546
3583
  const env = {
3547
- ...getProtectedUvEnv(baseEnv),
3584
+ ...getProtectedUvEnv(baseEnv, uvCacheDir),
3548
3585
  VIRTUAL_ENV: venvPath
3549
3586
  };
3550
3587
  const binDir = getVenvBinDir(venvPath);
@@ -3552,26 +3589,60 @@ function createVenvEnv(venvPath, baseEnv = process.env) {
3552
3589
  env.PATH = existingPath ? `${binDir}${import_path3.delimiter}${existingPath}` : binDir;
3553
3590
  return env;
3554
3591
  }
3592
+ async function readVenvPythonVersion(pyvenvCfgPath) {
3593
+ try {
3594
+ const content = await import_fs2.default.promises.readFile(pyvenvCfgPath, "utf-8");
3595
+ const match = content.match(/^version\s*=\s*(\d+)\.(\d+)/m);
3596
+ return match ? `${match[1]}.${match[2]}` : null;
3597
+ } catch {
3598
+ return null;
3599
+ }
3600
+ }
3555
3601
  async function ensureVenv({
3556
- pythonPath,
3602
+ pythonVersion,
3557
3603
  venvPath,
3558
3604
  uvPath,
3605
+ uvCacheDir,
3559
3606
  quiet
3560
3607
  }) {
3561
3608
  const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
3609
+ let venvExists = false;
3562
3610
  try {
3563
3611
  await import_fs2.default.promises.access(marker);
3564
- return;
3612
+ venvExists = true;
3565
3613
  } catch {
3566
3614
  }
3567
- await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
3568
- if (!quiet) {
3569
- console.log(`Creating virtual environment at "${venvPath}"...`);
3615
+ if (venvExists && pythonVersion.major != null && pythonVersion.minor != null) {
3616
+ const expected = `${pythonVersion.major}.${pythonVersion.minor}`;
3617
+ const cachedVersion = await readVenvPythonVersion(marker);
3618
+ if (cachedVersion && cachedVersion !== expected) {
3619
+ if (!quiet) {
3620
+ console.log(
3621
+ `Cached venv Python ${cachedVersion} differs from requested ${expected}, recreating...`
3622
+ );
3623
+ }
3624
+ await import_fs2.default.promises.rm(venvPath, { recursive: true, force: true });
3625
+ venvExists = false;
3626
+ }
3627
+ }
3628
+ if (venvExists) {
3629
+ (0, import_build_utils2.debug)(`Refreshing cached virtual environment at "${venvPath}"`);
3630
+ } else {
3631
+ await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
3632
+ if (!quiet) {
3633
+ console.log(`Creating virtual environment at "${venvPath}"...`);
3634
+ }
3570
3635
  }
3571
3636
  if (uvPath) {
3572
- await (0, import_execa2.default)(uvPath, ["venv", venvPath]);
3637
+ const args = ["venv", venvPath, "--allow-existing"];
3638
+ if (pythonVersion.major != null && pythonVersion.minor != null) {
3639
+ args.push("--python", `${pythonVersion.major}.${pythonVersion.minor}`);
3640
+ }
3641
+ await (0, import_execa2.default)(uvPath, args, {
3642
+ env: getProtectedUvEnv(process.env, uvCacheDir)
3643
+ });
3573
3644
  } else {
3574
- await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
3645
+ await (0, import_execa2.default)(pythonVersion.pythonPath, ["-m", "venv", venvPath]);
3575
3646
  }
3576
3647
  }
3577
3648
  function getVenvPythonBin(venvPath) {
@@ -3667,6 +3738,7 @@ function detectPlatform() {
3667
3738
 
3668
3739
  // src/version.ts
3669
3740
  var import_path4 = require("path");
3741
+ var import_fs3 = require("fs");
3670
3742
  var import_build_utils3 = require("@vercel/build-utils");
3671
3743
  var import_python_analysis = require("@vercel/python-analysis");
3672
3744
  function pythonVersionString(pv) {
@@ -3870,10 +3942,24 @@ function isDiscontinued({ discontinueDate }) {
3870
3942
  return discontinueDate !== void 0 && discontinueDate.getTime() <= today;
3871
3943
  }
3872
3944
  var installedPythonsCache = null;
3945
+ function getInstalledPythonsFromFilesystem(basePath = UV_PYTHON_PATH_PREFIX) {
3946
+ const result = /* @__PURE__ */ new Set();
3947
+ for (const opt of allOptions) {
3948
+ const binPath = (0, import_path4.join)(basePath, "bin", `python${opt.major}.${opt.minor}`);
3949
+ if ((0, import_fs3.existsSync)(binPath)) {
3950
+ result.add(pythonVersionString(opt));
3951
+ }
3952
+ }
3953
+ return result;
3954
+ }
3873
3955
  function getInstalledPythons() {
3874
3956
  if (installedPythonsCache !== null) {
3875
3957
  return installedPythonsCache;
3876
3958
  }
3959
+ if (process.env.VERCEL_BUILD_IMAGE) {
3960
+ installedPythonsCache = getInstalledPythonsFromFilesystem();
3961
+ return installedPythonsCache;
3962
+ }
3877
3963
  const uvPath = findUvInPath();
3878
3964
  if (!uvPath) {
3879
3965
  throw new import_build_utils3.NowBuildError({
@@ -3900,6 +3986,15 @@ function isInstalled(pv) {
3900
3986
  }
3901
3987
 
3902
3988
  // src/install.ts
3989
+ async function restoreCachedLock(cachedLockPath, projectDir) {
3990
+ if (!cachedLockPath)
3991
+ return;
3992
+ const targetLockPath = (0, import_path5.join)(projectDir, "uv.lock");
3993
+ if (import_fs4.default.existsSync(cachedLockPath) && !import_fs4.default.existsSync(targetLockPath)) {
3994
+ (0, import_build_utils4.debug)("Restoring cached uv.lock");
3995
+ await import_fs4.default.promises.copyFile(cachedLockPath, targetLockPath);
3996
+ }
3997
+ }
3903
3998
  var makeDependencyCheckCode = (dependency) => `
3904
3999
  from importlib import util
3905
4000
  dep = '${dependency}'.replace('-', '_')
@@ -4024,8 +4119,8 @@ async function ensureUvProject({
4024
4119
  pythonPackage,
4025
4120
  pythonVersion,
4026
4121
  uv,
4027
- generateLockFile = false,
4028
- requireBinaryWheels = false
4122
+ requireBinaryWheels = false,
4123
+ cachedLockPath
4029
4124
  }) {
4030
4125
  const { manifestType } = detectInstallSource(pythonPackage, rootDir);
4031
4126
  const manifest = pythonPackage.manifest;
@@ -4044,7 +4139,7 @@ async function ensureUvProject({
4044
4139
  lockPath = (0, import_path5.join)(rootDir, lockFile.path);
4045
4140
  projectDir = (0, import_path5.dirname)(lockPath);
4046
4141
  pyprojectPath = (0, import_path5.join)(projectDir, "pyproject.toml");
4047
- if (!import_fs3.default.existsSync(pyprojectPath)) {
4142
+ if (!import_fs4.default.existsSync(pyprojectPath)) {
4048
4143
  throw new Error(
4049
4144
  `Expected "pyproject.toml" next to "${lockFile.kind}" in "${projectDir}"`
4050
4145
  );
@@ -4071,24 +4166,19 @@ async function ensureUvProject({
4071
4166
  }
4072
4167
  const content = (0, import_python_analysis2.stringifyManifest)(manifest.data);
4073
4168
  pyprojectPath = (0, import_path5.join)(projectDir, "pyproject.toml");
4074
- await import_fs3.default.promises.writeFile(pyprojectPath, content);
4169
+ await import_fs4.default.promises.writeFile(pyprojectPath, content);
4075
4170
  }
4076
4171
  const workspaceLockFile = pythonPackage.workspaceLockFile;
4077
4172
  if (workspaceLockFile) {
4078
4173
  lockPath = (0, import_path5.join)(rootDir, workspaceLockFile.path);
4079
4174
  } else {
4175
+ await restoreCachedLock(cachedLockPath, projectDir);
4080
4176
  await uv.lock({
4081
4177
  projectDir,
4082
4178
  venvPath,
4083
4179
  ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4084
4180
  });
4085
- }
4086
- if (generateLockFile && !lockPath) {
4087
- await uv.lock({
4088
- projectDir,
4089
- venvPath,
4090
- ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4091
- });
4181
+ lockPath = (0, import_path5.join)(projectDir, "uv.lock");
4092
4182
  }
4093
4183
  } else {
4094
4184
  projectDir = workPath;
@@ -4103,14 +4193,15 @@ async function ensureUvProject({
4103
4193
  dependencies: []
4104
4194
  });
4105
4195
  const content = (0, import_python_analysis2.stringifyManifest)(minimalManifest);
4106
- await import_fs3.default.promises.writeFile(pyprojectPath, content);
4196
+ await import_fs4.default.promises.writeFile(pyprojectPath, content);
4197
+ await restoreCachedLock(cachedLockPath, projectDir);
4107
4198
  await uv.lock({
4108
4199
  projectDir,
4109
4200
  venvPath,
4110
4201
  ...requireBinaryWheels ? { noBuild: true, upgrade: true } : {}
4111
4202
  });
4112
4203
  }
4113
- const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : (0, import_path5.join)(projectDir, "uv.lock");
4204
+ const resolvedLockPath = lockPath && import_fs4.default.existsSync(lockPath) ? lockPath : (0, import_path5.join)(projectDir, "uv.lock");
4114
4205
  return {
4115
4206
  projectDir,
4116
4207
  pyprojectPath,
@@ -4136,7 +4227,7 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
4136
4227
  try {
4137
4228
  await (0, import_execa3.default)(uvPath, uvArgs, {
4138
4229
  cwd: workPath,
4139
- env: getProtectedUvEnv()
4230
+ env: getProtectedUvEnv(process.env, getUvCacheDir(workPath))
4140
4231
  });
4141
4232
  return;
4142
4233
  } catch (err) {
@@ -4211,12 +4302,12 @@ async function installRequirementsFile({
4211
4302
  }
4212
4303
 
4213
4304
  // src/dependency-externalizer.ts
4214
- var import_fs4 = __toESM(require("fs"));
4305
+ var import_fs5 = __toESM(require("fs"));
4215
4306
  var import_util = require("util");
4216
4307
  var import_path6 = require("path");
4217
4308
  var import_build_utils5 = require("@vercel/build-utils");
4218
4309
  var import_python_analysis3 = require("@vercel/python-analysis");
4219
- var readFile = (0, import_util.promisify)(import_fs4.default.readFile);
4310
+ var readFile = (0, import_util.promisify)(import_fs5.default.readFile);
4220
4311
  var LAMBDA_SIZE_THRESHOLD_BYTES = 245 * 1024 * 1024;
4221
4312
  var LAMBDA_EPHEMERAL_STORAGE_BYTES = 500 * 1024 * 1024;
4222
4313
  var PythonDependencyExternalizer = class {
@@ -4419,7 +4510,7 @@ To fix this, either:
4419
4510
  if (process.env.VERCEL_BUILD_IMAGE) {
4420
4511
  try {
4421
4512
  const uvBinaryPath = await getUvBinaryForBundling(this.pythonPath);
4422
- const uvStats = await import_fs4.default.promises.stat(uvBinaryPath);
4513
+ const uvStats = await import_fs5.default.promises.stat(uvBinaryPath);
4423
4514
  runtimeToolingOverhead = uvStats.size;
4424
4515
  } catch {
4425
4516
  runtimeToolingOverhead = 50 * 1024 * 1024;
@@ -4431,8 +4522,8 @@ To fix this, either:
4431
4522
  const isOutsideWorkPath = projectDirRel.startsWith("..") || uvLockRel.startsWith("..");
4432
4523
  if (isOutsideWorkPath) {
4433
4524
  const pyprojectPath = (0, import_path6.join)(this.uvProjectDir, "pyproject.toml");
4434
- const pyprojectStats = await import_fs4.default.promises.stat(pyprojectPath).catch(() => null);
4435
- const uvLockStats = await import_fs4.default.promises.stat(this.uvLockPath).catch(() => null);
4525
+ const pyprojectStats = await import_fs5.default.promises.stat(pyprojectPath).catch(() => null);
4526
+ const uvLockStats = await import_fs5.default.promises.stat(this.uvLockPath).catch(() => null);
4436
4527
  if (pyprojectStats)
4437
4528
  runtimeToolingOverhead += pyprojectStats.size;
4438
4529
  if (uvLockStats)
@@ -4487,9 +4578,9 @@ To fix this, either:
4487
4578
  const uvBinaryPath = await getUvBinaryForBundling(this.pythonPath);
4488
4579
  const uvBundleDir = (0, import_path6.join)(this.workPath, UV_BUNDLE_DIR);
4489
4580
  const uvLocalPath = (0, import_path6.join)(uvBundleDir, "uv");
4490
- await import_fs4.default.promises.mkdir(uvBundleDir, { recursive: true });
4491
- await import_fs4.default.promises.copyFile(uvBinaryPath, uvLocalPath);
4492
- await import_fs4.default.promises.chmod(uvLocalPath, 493);
4581
+ await import_fs5.default.promises.mkdir(uvBundleDir, { recursive: true });
4582
+ await import_fs5.default.promises.copyFile(uvBinaryPath, uvLocalPath);
4583
+ await import_fs5.default.promises.chmod(uvLocalPath, 493);
4493
4584
  const uvBundlePath = `${UV_BUNDLE_DIR}/uv`;
4494
4585
  files[uvBundlePath] = new import_build_utils5.FileFsRef({
4495
4586
  fsPath: uvLocalPath,
@@ -4662,7 +4753,7 @@ async function mirrorPackagesIntoVendor({
4662
4753
  const includeSet = includePackages ? new Set(includePackages.map(import_python_analysis3.normalizePackageName)) : null;
4663
4754
  const sitePackageDirs = await getVenvSitePackagesDirs(venvPath);
4664
4755
  for (const dir of sitePackageDirs) {
4665
- if (!import_fs4.default.existsSync(dir))
4756
+ if (!import_fs5.default.existsSync(dir))
4666
4757
  continue;
4667
4758
  const resolvedDir = (0, import_path6.resolve)(dir);
4668
4759
  const dirPrefix = resolvedDir + import_path6.sep;
@@ -4679,7 +4770,7 @@ async function mirrorPackagesIntoVendor({
4679
4770
  continue;
4680
4771
  }
4681
4772
  const srcFsPath = (0, import_path6.join)(dir, filePath);
4682
- if (!import_fs4.default.existsSync(srcFsPath)) {
4773
+ if (!import_fs5.default.existsSync(srcFsPath)) {
4683
4774
  continue;
4684
4775
  }
4685
4776
  const bundlePath = (0, import_path6.join)(vendorDirName, filePath).replace(/\\/g, "/");
@@ -4698,7 +4789,7 @@ async function calculateBundleSize(files) {
4698
4789
  const file = files[filePath];
4699
4790
  if ("fsPath" in file && file.fsPath) {
4700
4791
  try {
4701
- const stats = await import_fs4.default.promises.stat(file.fsPath);
4792
+ const stats = await import_fs5.default.promises.stat(file.fsPath);
4702
4793
  totalSize += stats.size;
4703
4794
  } catch (err) {
4704
4795
  console.warn(
@@ -4731,7 +4822,7 @@ async function calculatePerPackageSizes(venvPath) {
4731
4822
  const sizes = /* @__PURE__ */ new Map();
4732
4823
  const sitePackageDirs = await getVenvSitePackagesDirs(venvPath);
4733
4824
  for (const dir of sitePackageDirs) {
4734
- if (!import_fs4.default.existsSync(dir))
4825
+ if (!import_fs5.default.existsSync(dir))
4735
4826
  continue;
4736
4827
  const resolvedDir = (0, import_path6.resolve)(dir);
4737
4828
  const dirPrefix = resolvedDir + import_path6.sep;
@@ -4747,7 +4838,7 @@ async function calculatePerPackageSizes(venvPath) {
4747
4838
  continue;
4748
4839
  }
4749
4840
  try {
4750
- const stats = await import_fs4.default.promises.stat((0, import_path6.join)(dir, filePath));
4841
+ const stats = await import_fs5.default.promises.stat((0, import_path6.join)(dir, filePath));
4751
4842
  totalSize += stats.size;
4752
4843
  } catch {
4753
4844
  }
@@ -4759,7 +4850,7 @@ async function calculatePerPackageSizes(venvPath) {
4759
4850
  }
4760
4851
 
4761
4852
  // src/diagnostics.ts
4762
- var import_fs5 = __toESM(require("fs"));
4853
+ var import_fs6 = __toESM(require("fs"));
4763
4854
  var import_path7 = require("path");
4764
4855
  var import_build_utils6 = require("@vercel/build-utils");
4765
4856
  var import_python_analysis4 = require("@vercel/python-analysis");
@@ -4876,7 +4967,7 @@ async function generateProjectManifest({
4876
4967
  const directEntries = [];
4877
4968
  const transitiveEntries = [];
4878
4969
  {
4879
- const content = await import_fs5.default.promises.readFile(uvLockPath, "utf-8");
4970
+ const content = await import_fs6.default.promises.readFile(uvLockPath, "utf-8");
4880
4971
  const uvLock = (0, import_python_analysis4.parseUvLock)(content, uvLockPath);
4881
4972
  const projectName = project?.name;
4882
4973
  const excludeSet = new Set(
@@ -4980,15 +5071,15 @@ async function generateProjectManifest({
4980
5071
  ]
4981
5072
  };
4982
5073
  const outPath = (0, import_path7.join)(workPath, DIAGNOSTICS_PATH);
4983
- await import_fs5.default.promises.mkdir((0, import_path7.dirname)(outPath), { recursive: true });
4984
- await import_fs5.default.promises.writeFile(outPath, JSON.stringify(manifest, null, 2));
5074
+ await import_fs6.default.promises.mkdir((0, import_path7.dirname)(outPath), { recursive: true });
5075
+ await import_fs6.default.promises.writeFile(outPath, JSON.stringify(manifest, null, 2));
4985
5076
  }
4986
5077
  var diagnostics = async ({
4987
5078
  workPath
4988
5079
  }) => {
4989
5080
  try {
4990
5081
  const manifestPath = (0, import_path7.join)(workPath, DIAGNOSTICS_PATH);
4991
- const data = await import_fs5.default.promises.readFile(manifestPath, "utf-8");
5082
+ const data = await import_fs6.default.promises.readFile(manifestPath, "utf-8");
4992
5083
  return {
4993
5084
  [MANIFEST_FILENAME]: new import_build_utils6.FileBlob({ data })
4994
5085
  };
@@ -5002,14 +5093,14 @@ var diagnostics = async ({
5002
5093
 
5003
5094
  // src/start-dev-server.ts
5004
5095
  var import_child_process2 = require("child_process");
5005
- var import_fs7 = require("fs");
5096
+ var import_fs8 = require("fs");
5006
5097
  var import_path9 = require("path");
5007
5098
  var import_build_utils10 = require("@vercel/build-utils");
5008
5099
  var import_get_port = __toESM(require_get_port());
5009
5100
  var import_is_port_reachable = __toESM(require_is_port_reachable());
5010
5101
 
5011
5102
  // src/entrypoint.ts
5012
- var import_fs6 = __toESM(require("fs"));
5103
+ var import_fs7 = __toESM(require("fs"));
5013
5104
  var import_path8 = require("path");
5014
5105
  var import_build_utils7 = require("@vercel/build-utils");
5015
5106
  var import_build_utils8 = require("@vercel/build-utils");
@@ -5036,7 +5127,7 @@ function getCandidateEntrypointsInDirs(dirs) {
5036
5127
  }
5037
5128
  async function fileExists(filePath) {
5038
5129
  try {
5039
- const stat = await import_fs6.default.promises.stat(filePath);
5130
+ const stat = await import_fs7.default.promises.stat(filePath);
5040
5131
  return stat.isFile();
5041
5132
  } catch {
5042
5133
  return false;
@@ -5046,7 +5137,7 @@ async function checkEntrypoint(workPath, relPath) {
5046
5137
  const absPath = (0, import_path8.join)(workPath, relPath);
5047
5138
  if (!await fileExists(absPath))
5048
5139
  return null;
5049
- const content = await import_fs6.default.promises.readFile(absPath, "utf-8");
5140
+ const content = await import_fs7.default.promises.readFile(absPath, "utf-8");
5050
5141
  return (0, import_python_analysis5.findAppOrHandler)(content);
5051
5142
  }
5052
5143
  async function getPyprojectEntrypoint(workPath) {
@@ -5084,7 +5175,7 @@ async function findValidEntrypoint(workPath, candidates) {
5084
5175
  async function checkDjangoManage(workPath) {
5085
5176
  const managePath = (0, import_path8.join)(workPath, "manage.py");
5086
5177
  try {
5087
- const content = await import_fs6.default.promises.readFile(managePath, "utf-8");
5178
+ const content = await import_fs7.default.promises.readFile(managePath, "utf-8");
5088
5179
  if (!content.includes("DJANGO_SETTINGS_MODULE"))
5089
5180
  return false;
5090
5181
  (0, import_build_utils8.debug)(`Found Django manage.py with DJANGO_SETTINGS_MODULE at ${workPath}`);
@@ -5095,7 +5186,7 @@ async function checkDjangoManage(workPath) {
5095
5186
  }
5096
5187
  async function getSubdirectories(workPath) {
5097
5188
  try {
5098
- const entries = await import_fs6.default.promises.readdir(workPath, {
5189
+ const entries = await import_fs7.default.promises.readdir(workPath, {
5099
5190
  withFileTypes: true
5100
5191
  });
5101
5192
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name).sort();
@@ -5269,10 +5360,10 @@ async function syncDependencies({
5269
5360
  }
5270
5361
  if (manifest?.origin && manifestType === "pyproject.toml") {
5271
5362
  const syncDir = (0, import_path9.join)(workPath, ".vercel", "python", "sync");
5272
- (0, import_fs7.mkdirSync)(syncDir, { recursive: true });
5363
+ (0, import_fs8.mkdirSync)(syncDir, { recursive: true });
5273
5364
  const tempPyproject = (0, import_path9.join)(syncDir, "pyproject.toml");
5274
5365
  const content = (0, import_python_analysis6.stringifyManifest)(manifest.data);
5275
- (0, import_fs7.writeFileSync)(tempPyproject, content, "utf8");
5366
+ (0, import_fs8.writeFileSync)(tempPyproject, content, "utf8");
5276
5367
  manifestPath = tempPyproject;
5277
5368
  (0, import_build_utils10.debug)(
5278
5369
  `Wrote converted ${manifest.origin.kind} manifest to ${tempPyproject}`
@@ -5425,7 +5516,7 @@ async function doInstallVercelRuntime({
5425
5516
  onStdout,
5426
5517
  onStderr
5427
5518
  }) {
5428
- (0, import_fs7.mkdirSync)(targetDir, { recursive: true });
5519
+ (0, import_fs8.mkdirSync)(targetDir, { recursive: true });
5429
5520
  const localRuntimeDir = (0, import_path9.join)(
5430
5521
  __dirname,
5431
5522
  "..",
@@ -5434,14 +5525,14 @@ async function doInstallVercelRuntime({
5434
5525
  "python",
5435
5526
  "vercel-runtime"
5436
5527
  );
5437
- const isLocalDev = (0, import_fs7.existsSync)((0, import_path9.join)(localRuntimeDir, "pyproject.toml"));
5528
+ const isLocalDev = (0, import_fs8.existsSync)((0, import_path9.join)(localRuntimeDir, "pyproject.toml"));
5438
5529
  const runtimeDep = env.VERCEL_RUNTIME_PYTHON || (isLocalDev ? localRuntimeDir : `vercel-runtime==${VERCEL_RUNTIME_VERSION}`);
5439
5530
  if (!isLocalDev && !env.VERCEL_RUNTIME_PYTHON) {
5440
5531
  const distInfo = (0, import_path9.join)(
5441
5532
  targetDir,
5442
5533
  `vercel_runtime-${VERCEL_RUNTIME_VERSION}.dist-info`
5443
5534
  );
5444
- if ((0, import_fs7.existsSync)(distInfo)) {
5535
+ if ((0, import_fs8.existsSync)(distInfo)) {
5445
5536
  (0, import_build_utils10.debug)(
5446
5537
  `vercel-runtime ${VERCEL_RUNTIME_VERSION} already installed, skipping`
5447
5538
  );
@@ -5521,7 +5612,7 @@ async function doInstallVercelWorkers({
5521
5612
  onStdout,
5522
5613
  onStderr
5523
5614
  }) {
5524
- (0, import_fs7.mkdirSync)(targetDir, { recursive: true });
5615
+ (0, import_fs8.mkdirSync)(targetDir, { recursive: true });
5525
5616
  const localWorkersDir = (0, import_path9.join)(
5526
5617
  __dirname,
5527
5618
  "..",
@@ -5530,14 +5621,14 @@ async function doInstallVercelWorkers({
5530
5621
  "python",
5531
5622
  "vercel-workers"
5532
5623
  );
5533
- const isLocalDev = (0, import_fs7.existsSync)((0, import_path9.join)(localWorkersDir, "pyproject.toml"));
5624
+ const isLocalDev = (0, import_fs8.existsSync)((0, import_path9.join)(localWorkersDir, "pyproject.toml"));
5534
5625
  const workersDep = env.VERCEL_WORKERS_PYTHON || (isLocalDev ? localWorkersDir : `vercel-workers==${VERCEL_WORKERS_VERSION}`);
5535
5626
  if (!isLocalDev && !env.VERCEL_WORKERS_PYTHON) {
5536
5627
  const distInfo = (0, import_path9.join)(
5537
5628
  targetDir,
5538
5629
  `vercel_workers-${VERCEL_WORKERS_VERSION}.dist-info`
5539
5630
  );
5540
- if ((0, import_fs7.existsSync)(distInfo)) {
5631
+ if ((0, import_fs8.existsSync)(distInfo)) {
5541
5632
  (0, import_build_utils10.debug)(
5542
5633
  `vercel-workers ${VERCEL_WORKERS_VERSION} already installed, skipping`
5543
5634
  );
@@ -5627,10 +5718,10 @@ function installGlobalCleanupHandlers() {
5627
5718
  function createDevShim(workPath, entry, modulePath, serviceName, framework, variableName) {
5628
5719
  try {
5629
5720
  const vercelPythonDir = serviceName ? (0, import_path9.join)(workPath, ".vercel", "python", "services", serviceName) : (0, import_path9.join)(workPath, ".vercel", "python");
5630
- (0, import_fs7.mkdirSync)(vercelPythonDir, { recursive: true });
5721
+ (0, import_fs8.mkdirSync)(vercelPythonDir, { recursive: true });
5631
5722
  let qualifiedModule = modulePath;
5632
5723
  let extraPythonPath;
5633
- if ((0, import_fs7.existsSync)((0, import_path9.join)(workPath, "__init__.py"))) {
5724
+ if ((0, import_fs8.existsSync)((0, import_path9.join)(workPath, "__init__.py"))) {
5634
5725
  const pkgName = (0, import_path9.basename)(workPath);
5635
5726
  qualifiedModule = `${pkgName}.${modulePath}`;
5636
5727
  extraPythonPath = (0, import_path9.dirname)(workPath);
@@ -5643,9 +5734,9 @@ function createDevShim(workPath, entry, modulePath, serviceName, framework, vari
5643
5734
  "templates",
5644
5735
  `${DEV_SHIM_MODULE}.py`
5645
5736
  );
5646
- const template = (0, import_fs7.readFileSync)(templatePath, "utf8");
5737
+ const template = (0, import_fs8.readFileSync)(templatePath, "utf8");
5647
5738
  const shimSource = template.replace(/__VC_DEV_MODULE_NAME__/g, qualifiedModule).replace(/__VC_DEV_ENTRY_ABS__/g, entryAbs).replace(/__VC_DEV_FRAMEWORK__/g, framework).replace(/__VC_DEV_VARIABLE_NAME__/g, variableName);
5648
- (0, import_fs7.writeFileSync)(shimPath, shimSource, "utf8");
5739
+ (0, import_fs8.writeFileSync)(shimPath, shimSource, "utf8");
5649
5740
  (0, import_build_utils10.debug)(`Prepared Python dev shim at ${shimPath}`);
5650
5741
  return {
5651
5742
  module: DEV_SHIM_MODULE,
@@ -5664,7 +5755,12 @@ async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath)
5664
5755
  return { command: pythonCmd, args: [] };
5665
5756
  }
5666
5757
  const venvPath = (0, import_path9.join)(workPath, ".venv");
5667
- await ensureVenv({ pythonPath: systemPython, venvPath, uvPath, quiet: true });
5758
+ await ensureVenv({
5759
+ pythonVersion: { pythonPath: systemPython },
5760
+ venvPath,
5761
+ uvPath,
5762
+ quiet: true
5763
+ });
5668
5764
  (0, import_build_utils10.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
5669
5765
  const pythonBin = getVenvPythonBin(venvPath);
5670
5766
  const binDir = getVenvBinDir(venvPath);
@@ -5947,7 +6043,7 @@ var matplotlibQuirk = {
5947
6043
  };
5948
6044
 
5949
6045
  // src/quirks/litellm.ts
5950
- var import_fs8 = __toESM(require("fs"));
6046
+ var import_fs9 = __toESM(require("fs"));
5951
6047
  var import_path10 = require("path");
5952
6048
  var import_build_utils11 = require("@vercel/build-utils");
5953
6049
  var LAMBDA_ROOT = "/var/task";
@@ -5961,7 +6057,7 @@ async function findConfigFile(workPath) {
5961
6057
  for (const name of CONFIG_CANDIDATES) {
5962
6058
  const candidate = (0, import_path10.join)(workPath, name);
5963
6059
  try {
5964
- await import_fs8.default.promises.access(candidate);
6060
+ await import_fs9.default.promises.access(candidate);
5965
6061
  return name;
5966
6062
  } catch {
5967
6063
  }
@@ -5983,7 +6079,7 @@ var litellmQuirk = {
5983
6079
  "schema.prisma"
5984
6080
  );
5985
6081
  try {
5986
- await import_fs8.default.promises.access(schemaPath);
6082
+ await import_fs9.default.promises.access(schemaPath);
5987
6083
  (0, import_build_utils11.debug)(`LiteLLM quirk: found schema at ${schemaPath}`);
5988
6084
  buildEnv.PRISMA_SCHEMA_PATH = schemaPath;
5989
6085
  break;
@@ -6010,7 +6106,7 @@ var litellmQuirk = {
6010
6106
  };
6011
6107
 
6012
6108
  // src/quirks/prisma.ts
6013
- var import_fs9 = __toESM(require("fs"));
6109
+ var import_fs10 = __toESM(require("fs"));
6014
6110
  var import_path11 = require("path");
6015
6111
  var import_execa4 = __toESM(require_execa());
6016
6112
  var import_build_utils12 = require("@vercel/build-utils");
@@ -6053,7 +6149,7 @@ async function findUserSchema(workPath) {
6053
6149
  if (envPath) {
6054
6150
  const resolved = (0, import_path11.isAbsolute)(envPath) ? envPath : (0, import_path11.join)(workPath, envPath);
6055
6151
  try {
6056
- await import_fs9.default.promises.access(resolved);
6152
+ await import_fs10.default.promises.access(resolved);
6057
6153
  return resolved;
6058
6154
  } catch {
6059
6155
  (0, import_build_utils12.debug)(`PRISMA_SCHEMA_PATH=${envPath} not found at ${resolved}`);
@@ -6066,7 +6162,7 @@ async function findUserSchema(workPath) {
6066
6162
  ];
6067
6163
  for (const candidate of candidates) {
6068
6164
  try {
6069
- await import_fs9.default.promises.access(candidate);
6165
+ await import_fs10.default.promises.access(candidate);
6070
6166
  return candidate;
6071
6167
  } catch {
6072
6168
  }
@@ -6077,7 +6173,7 @@ async function collectFiles(dir, base) {
6077
6173
  const result = [];
6078
6174
  let entries;
6079
6175
  try {
6080
- entries = await import_fs9.default.promises.readdir(dir, { withFileTypes: true });
6176
+ entries = await import_fs10.default.promises.readdir(dir, { withFileTypes: true });
6081
6177
  } catch {
6082
6178
  return result;
6083
6179
  }
@@ -6102,7 +6198,7 @@ async function cleanCacheArtifacts(cacheDir, extras = []) {
6102
6198
  ];
6103
6199
  for (const p of paths) {
6104
6200
  try {
6105
- await import_fs9.default.promises.rm(p, { recursive: true, force: true });
6201
+ await import_fs10.default.promises.rm(p, { recursive: true, force: true });
6106
6202
  } catch (err) {
6107
6203
  console.warn(
6108
6204
  `could not clean up ${p}: ${err instanceof Error ? err.message : String(err)}`
@@ -6136,7 +6232,7 @@ var prismaQuirk = {
6136
6232
  let sitePackages;
6137
6233
  for (const dir of sitePackagesDirs) {
6138
6234
  try {
6139
- await import_fs9.default.promises.access((0, import_path11.join)(dir, "prisma"));
6235
+ await import_fs10.default.promises.access((0, import_path11.join)(dir, "prisma"));
6140
6236
  sitePackages = dir;
6141
6237
  break;
6142
6238
  } catch {
@@ -6149,14 +6245,14 @@ var prismaQuirk = {
6149
6245
  return {};
6150
6246
  }
6151
6247
  const cacheDir = (0, import_path11.join)(sitePackages, "prisma", "__bincache__");
6152
- await import_fs9.default.promises.mkdir(cacheDir, { recursive: true });
6248
+ await import_fs10.default.promises.mkdir(cacheDir, { recursive: true });
6153
6249
  const generateEnv = {
6154
6250
  ...pythonEnv,
6155
6251
  PRISMA_BINARY_CACHE_DIR: cacheDir
6156
6252
  };
6157
6253
  const generatedDir = (0, import_path11.join)(workPath, "_prisma_generated");
6158
6254
  const dummySchemaPath = (0, import_path11.join)(workPath, DUMMY_SCHEMA_NAME);
6159
- await import_fs9.default.promises.writeFile(
6255
+ await import_fs10.default.promises.writeFile(
6160
6256
  dummySchemaPath,
6161
6257
  buildDummySchema(generatedDir)
6162
6258
  );
@@ -6187,18 +6283,18 @@ var prismaQuirk = {
6187
6283
  const nodeModulesDir = (0, import_path11.join)(cacheDir, "node_modules", "prisma");
6188
6284
  let engineCopied = false;
6189
6285
  try {
6190
- const entries = await import_fs9.default.promises.readdir(nodeModulesDir);
6286
+ const entries = await import_fs10.default.promises.readdir(nodeModulesDir);
6191
6287
  for (const entry of entries) {
6192
6288
  if (!entry.startsWith(srcBinaryPrefix))
6193
6289
  continue;
6194
6290
  const srcPath = (0, import_path11.join)(nodeModulesDir, entry);
6195
6291
  const destPath = (0, import_path11.join)(cacheDir, runtimeName);
6196
6292
  try {
6197
- await import_fs9.default.promises.access(destPath);
6293
+ await import_fs10.default.promises.access(destPath);
6198
6294
  (0, import_build_utils12.debug)(`Engine binary: ${runtimeName} already exists, skipping`);
6199
6295
  } catch {
6200
6296
  (0, import_build_utils12.debug)(`Engine binary: copying ${entry} -> ${runtimeName}`);
6201
- await import_fs9.default.promises.copyFile(srcPath, destPath);
6297
+ await import_fs10.default.promises.copyFile(srcPath, destPath);
6202
6298
  }
6203
6299
  engineCopied = true;
6204
6300
  }
@@ -6216,15 +6312,15 @@ var prismaQuirk = {
6216
6312
  });
6217
6313
  }
6218
6314
  const shimPath = (0, import_path11.join)(cacheDir, "openssl");
6219
- await import_fs9.default.promises.writeFile(
6315
+ await import_fs10.default.promises.writeFile(
6220
6316
  shimPath,
6221
6317
  `#!/bin/sh
6222
6318
  echo "OpenSSL ${RUNTIME_OPENSSL_VERSION}.0 1 Jan 2024 (Library: OpenSSL ${RUNTIME_OPENSSL_VERSION}.0)"
6223
6319
  `
6224
6320
  );
6225
- await import_fs9.default.promises.chmod(shimPath, 493);
6321
+ await import_fs10.default.promises.chmod(shimPath, 493);
6226
6322
  for (const p of [generatedDir, dummySchemaPath]) {
6227
- await import_fs9.default.promises.rm(p, { recursive: true, force: true });
6323
+ await import_fs10.default.promises.rm(p, { recursive: true, force: true });
6228
6324
  }
6229
6325
  await cleanCacheArtifacts(cacheDir);
6230
6326
  const generateMode = (process.env.VERCEL_PRISMA_GENERATE_CLIENT ?? "auto").toLowerCase();
@@ -6397,12 +6493,12 @@ async function runQuirks(ctx) {
6397
6493
  }
6398
6494
 
6399
6495
  // src/django.ts
6400
- var import_fs10 = __toESM(require("fs"));
6496
+ var import_fs11 = __toESM(require("fs"));
6401
6497
  var import_path12 = require("path");
6402
6498
  var import_execa5 = __toESM(require_execa());
6403
6499
  var import_build_utils14 = require("@vercel/build-utils");
6404
6500
  var scriptPath = (0, import_path12.join)(__dirname, "..", "templates", "vc_django_settings.py");
6405
- var script = import_fs10.default.readFileSync(scriptPath, "utf-8");
6501
+ var script = import_fs11.default.readFileSync(scriptPath, "utf-8");
6406
6502
  async function getDjangoSettings(projectDir, env) {
6407
6503
  const { stdout } = await (0, import_execa5.default)("python", ["-c", script], {
6408
6504
  env,
@@ -6433,7 +6529,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6433
6529
  ...installedApps.map((app) => (0, import_path12.join)(workPath, ...app.split("."), "static")),
6434
6530
  // TODO: Deal with optional prefixes in STATICFILES_DIRS.
6435
6531
  ...staticfilesDirs.map((d) => Array.isArray(d) ? d[1] : d)
6436
- ].filter((d) => import_fs10.default.existsSync(d));
6532
+ ].filter((d) => import_fs11.default.existsSync(d));
6437
6533
  if (storageBackend.startsWith("storages.backends.")) {
6438
6534
  console.log(
6439
6535
  "django-storages detected \u2014 running collectstatic with original settings"
@@ -6457,7 +6553,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6457
6553
  }
6458
6554
  const staticUrlPath = staticUrl.replace(/^\/|\/$/g, "") || "static";
6459
6555
  const staticOutputDir = (0, import_path12.join)(outputStaticDir, staticUrlPath);
6460
- await import_fs10.default.promises.mkdir(staticOutputDir, { recursive: true });
6556
+ await import_fs11.default.promises.mkdir(staticOutputDir, { recursive: true });
6461
6557
  const shimPath = (0, import_path12.join)(workPath, "_vercel_collectstatic_settings.py");
6462
6558
  const shimLines = [
6463
6559
  `from ${settingsModule} import *`,
@@ -6466,7 +6562,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6466
6562
  if (whitenoiseUseFinders) {
6467
6563
  shimLines.push(`WHITENOISE_USE_FINDERS = False`);
6468
6564
  }
6469
- await import_fs10.default.promises.writeFile(shimPath, shimLines.join("\n") + "\n");
6565
+ await import_fs11.default.promises.writeFile(shimPath, shimLines.join("\n") + "\n");
6470
6566
  try {
6471
6567
  console.log("Running collectstatic...");
6472
6568
  await (0, import_execa5.default)(pythonPath, ["manage.py", "collectstatic", "--noinput"], {
@@ -6477,7 +6573,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6477
6573
  cwd: workPath
6478
6574
  });
6479
6575
  } finally {
6480
- await import_fs10.default.promises.unlink(shimPath).catch(() => {
6576
+ await import_fs11.default.promises.unlink(shimPath).catch(() => {
6481
6577
  });
6482
6578
  }
6483
6579
  const MANIFEST_STORAGE_BACKENDS = [
@@ -6489,8 +6585,8 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6489
6585
  const manifestSrc = (0, import_path12.join)(staticOutputDir, "staticfiles.json");
6490
6586
  const resolvedStaticRoot = (0, import_path12.resolve)(workPath, staticRoot);
6491
6587
  const manifestDest = (0, import_path12.join)(resolvedStaticRoot, "staticfiles.json");
6492
- await import_fs10.default.promises.mkdir(resolvedStaticRoot, { recursive: true });
6493
- await import_fs10.default.promises.copyFile(manifestSrc, manifestDest);
6588
+ await import_fs11.default.promises.mkdir(resolvedStaticRoot, { recursive: true });
6589
+ await import_fs11.default.promises.copyFile(manifestSrc, manifestDest);
6494
6590
  manifestRelPath = (0, import_path12.relative)(workPath, manifestDest);
6495
6591
  (0, import_build_utils14.debug)(`Copied staticfiles.json to ${manifestDest} for Lambda bundle`);
6496
6592
  }
@@ -6504,7 +6600,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
6504
6600
 
6505
6601
  // src/index.ts
6506
6602
  var import_python_analysis9 = require("@vercel/python-analysis");
6507
- var writeFile = import_fs11.default.promises.writeFile;
6603
+ var writeFile = import_fs12.default.promises.writeFile;
6508
6604
  var version = -1;
6509
6605
  async function runFrameworkHook(framework, ctx) {
6510
6606
  const hook = framework ? frameworkHooks[framework] : void 0;
@@ -6676,10 +6772,19 @@ var build = async ({
6676
6772
  );
6677
6773
  }
6678
6774
  const venvPath = service?.name ? (0, import_path13.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path13.join)(workPath, ".vercel", "python", ".venv");
6775
+ const uvCacheDir = getUvCacheDir(workPath);
6776
+ const hasCachedVenv = import_fs12.default.existsSync((0, import_path13.join)(venvPath, "pyvenv.cfg"));
6777
+ const hasCachedUv = import_fs12.default.existsSync(uvCacheDir);
6778
+ if (hasCachedVenv || hasCachedUv) {
6779
+ (0, import_build_utils15.debug)(
6780
+ `Build cache detected: venv=${hasCachedVenv}, uv-cache=${hasCachedUv}`
6781
+ );
6782
+ }
6679
6783
  await builderSpan.child("vc.builder.python.venv").trace(async () => {
6680
6784
  await ensureVenv({
6681
- pythonPath: pythonVersion.pythonPath,
6682
- venvPath
6785
+ pythonVersion,
6786
+ venvPath,
6787
+ uvCacheDir
6683
6788
  });
6684
6789
  });
6685
6790
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -6708,13 +6813,13 @@ var build = async ({
6708
6813
  }
6709
6814
  }
6710
6815
  const baseEnv = spawnEnv || process.env;
6711
- const pythonEnv = createVenvEnv(venvPath, baseEnv);
6816
+ const pythonEnv = createVenvEnv(venvPath, baseEnv, uvCacheDir);
6712
6817
  pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
6713
6818
  let assumeDepsInstalled = false;
6714
6819
  let uv;
6715
6820
  try {
6716
6821
  const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
6717
- uv = new UvRunner(uvPath);
6822
+ uv = new UvRunner(uvPath, uvCacheDir);
6718
6823
  const uvVersionOutput = (0, import_child_process3.execSync)(`${uvPath} --version`, {
6719
6824
  encoding: "utf8"
6720
6825
  }).trim();
@@ -6732,6 +6837,13 @@ var build = async ({
6732
6837
  installCommand: projectInstallCommand || void 0
6733
6838
  }).trace(async () => {
6734
6839
  if (projectInstallCommand) {
6840
+ await import_fs12.default.promises.rm(venvPath, { recursive: true, force: true });
6841
+ await ensureVenv({
6842
+ pythonVersion,
6843
+ venvPath,
6844
+ uvCacheDir,
6845
+ quiet: true
6846
+ });
6735
6847
  console.log(
6736
6848
  `Running "install" command: \`${projectInstallCommand}\`...`
6737
6849
  );
@@ -6742,18 +6854,21 @@ var build = async ({
6742
6854
  assumeDepsInstalled = true;
6743
6855
  hasCustomCommand = true;
6744
6856
  } else {
6745
- assumeDepsInstalled = await runPyprojectScript(
6857
+ const hasCustomScript = await runPyprojectScript(
6746
6858
  workPath,
6747
6859
  ["vercel-install", "now-install", "install"],
6748
6860
  pythonEnv,
6749
6861
  /* useUserVirtualEnv */
6750
6862
  false
6751
6863
  );
6752
- if (assumeDepsInstalled) {
6864
+ if (hasCustomScript) {
6865
+ assumeDepsInstalled = true;
6753
6866
  hasCustomCommand = true;
6754
6867
  }
6755
6868
  }
6756
6869
  if (!assumeDepsInstalled) {
6870
+ const lockCacheKey = service?.name ? `uv.lock.${service.name}` : "uv.lock";
6871
+ const cachedLockPath = (0, import_path13.join)(uvCacheDir, lockCacheKey);
6757
6872
  const { projectDir, lockPath, lockFileProvidedByUser } = await ensureUvProject({
6758
6873
  workPath,
6759
6874
  rootDir,
@@ -6761,8 +6876,8 @@ var build = async ({
6761
6876
  pythonPackage,
6762
6877
  pythonVersion: pythonVersionString(pythonVersion),
6763
6878
  uv,
6764
- generateLockFile: true,
6765
- requireBinaryWheels: false
6879
+ requireBinaryWheels: false,
6880
+ cachedLockPath
6766
6881
  });
6767
6882
  uvLockPath = lockPath;
6768
6883
  uvProjectDir = projectDir;
@@ -6773,6 +6888,10 @@ var build = async ({
6773
6888
  frozen: lockFileProvidedByUser,
6774
6889
  locked: !lockFileProvidedByUser
6775
6890
  });
6891
+ if (lockPath && import_fs12.default.existsSync(lockPath)) {
6892
+ await import_fs12.default.promises.mkdir(uvCacheDir, { recursive: true });
6893
+ await import_fs12.default.promises.copyFile(lockPath, cachedLockPath);
6894
+ }
6776
6895
  }
6777
6896
  });
6778
6897
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -6841,7 +6960,7 @@ var build = async ({
6841
6960
  const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
6842
6961
  if (handlerFunction) {
6843
6962
  const entrypointPath = (0, import_path13.join)(workPath, entrypoint);
6844
- const source = await import_fs11.default.promises.readFile(entrypointPath, "utf-8");
6963
+ const source = await import_fs12.default.promises.readFile(entrypointPath, "utf-8");
6845
6964
  const found = await (0, import_python_analysis9.containsTopLevelCallable)(source, handlerFunction);
6846
6965
  if (!found) {
6847
6966
  throw new import_build_utils15.NowBuildError({
@@ -7008,6 +7127,29 @@ from vercel_runtime.vc_init import vc_handler
7008
7127
  }
7009
7128
  };
7010
7129
  };
7130
+ var prepareCache = async ({
7131
+ repoRootPath,
7132
+ workPath
7133
+ }) => {
7134
+ if (process.env.VERCEL_PYTHON_PREPARE_CACHE !== "1") {
7135
+ return {};
7136
+ }
7137
+ const root = repoRootPath || workPath;
7138
+ const ignore = ["**/*.pyc", "**/__pycache__/**"];
7139
+ const uvCacheDir = getUvCacheDir(workPath);
7140
+ try {
7141
+ const uvPath = findUvInPath();
7142
+ if (uvPath) {
7143
+ const uv = new UvRunner(uvPath, uvCacheDir);
7144
+ await uv.cachePrune();
7145
+ }
7146
+ } catch {
7147
+ }
7148
+ return (0, import_build_utils15.glob)("**/.vercel/python/{.venv,services/*/.venv,cache/uv}/**", {
7149
+ cwd: root,
7150
+ ignore
7151
+ });
7152
+ };
7011
7153
  var shouldServe = (opts) => {
7012
7154
  const framework = opts.config.framework;
7013
7155
  if ((0, import_build_utils15.isPythonFramework)(framework)) {
@@ -7046,6 +7188,7 @@ function hasProp(obj, key) {
7046
7188
  downloadFilesInWorkPath,
7047
7189
  installRequirement,
7048
7190
  installRequirementsFile,
7191
+ prepareCache,
7049
7192
  runFrameworkHook,
7050
7193
  shouldServe,
7051
7194
  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.1",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -36,7 +36,7 @@
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",
39
+ "@vercel/build-utils": "13.14.2",
40
40
  "@vercel/error-utils": "2.0.3",
41
41
  "@vercel/python-runtime": "0.12.0"
42
42
  },