@vercel/python 6.1.1 → 6.1.3

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 +645 -504
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ var require_windows = __commonJS({
48
48
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module2) {
49
49
  module2.exports = isexe;
50
50
  isexe.sync = sync;
51
- var fs6 = require("fs");
51
+ var fs5 = require("fs");
52
52
  function checkPathExt(path, options) {
53
53
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
54
54
  if (!pathext) {
@@ -73,12 +73,12 @@ var require_windows = __commonJS({
73
73
  return checkPathExt(path, options);
74
74
  }
75
75
  function isexe(path, options, cb) {
76
- fs6.stat(path, function(er, stat) {
76
+ fs5.stat(path, function(er, stat) {
77
77
  cb(er, er ? false : checkStat(stat, path, options));
78
78
  });
79
79
  }
80
80
  function sync(path, options) {
81
- return checkStat(fs6.statSync(path), path, options);
81
+ return checkStat(fs5.statSync(path), path, options);
82
82
  }
83
83
  }
84
84
  });
@@ -88,14 +88,14 @@ var require_mode = __commonJS({
88
88
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module2) {
89
89
  module2.exports = isexe;
90
90
  isexe.sync = sync;
91
- var fs6 = require("fs");
91
+ var fs5 = require("fs");
92
92
  function isexe(path, options, cb) {
93
- fs6.stat(path, function(er, stat) {
93
+ fs5.stat(path, function(er, stat) {
94
94
  cb(er, er ? false : checkStat(stat, options));
95
95
  });
96
96
  }
97
97
  function sync(path, options) {
98
- return checkStat(fs6.statSync(path), options);
98
+ return checkStat(fs5.statSync(path), options);
99
99
  }
100
100
  function checkStat(stat, options) {
101
101
  return stat.isFile() && checkMode(stat, options);
@@ -119,7 +119,7 @@ var require_mode = __commonJS({
119
119
  // ../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js
120
120
  var require_isexe = __commonJS({
121
121
  "../../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module2) {
122
- var fs6 = require("fs");
122
+ var fs5 = require("fs");
123
123
  var core;
124
124
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
125
125
  core = require_windows();
@@ -395,7 +395,7 @@ var require_shebang_command = __commonJS({
395
395
  var require_readShebang = __commonJS({
396
396
  "../../node_modules/.pnpm/cross-spawn@6.0.5/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module2) {
397
397
  "use strict";
398
- var fs6 = require("fs");
398
+ var fs5 = require("fs");
399
399
  var shebangCommand = require_shebang_command();
400
400
  function readShebang(command) {
401
401
  const size = 150;
@@ -408,9 +408,9 @@ var require_readShebang = __commonJS({
408
408
  }
409
409
  let fd;
410
410
  try {
411
- fd = fs6.openSync(command, "r");
412
- fs6.readSync(fd, buffer, 0, size, 0);
413
- fs6.closeSync(fd);
411
+ fd = fs5.openSync(command, "r");
412
+ fs5.readSync(fd, buffer, 0, size, 0);
413
+ fs5.closeSync(fd);
414
414
  } catch (e) {
415
415
  }
416
416
  return shebangCommand(buffer.toString());
@@ -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 isWin2 = 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 (!isWin2) {
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 (isWin2) {
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 isWin2 = 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 (!isWin2) {
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 (isWin2 && 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 (isWin2 && status === 1 && !parsed.file) {
1613
+ if (isWin3 && status === 1 && !parsed.file) {
1614
1614
  return notFoundError(parsed.original, "spawnSync");
1615
1615
  }
1616
1616
  return null;
@@ -1895,9 +1895,9 @@ var require_pump = __commonJS({
1895
1895
  "../../node_modules/.pnpm/pump@3.0.2/node_modules/pump/index.js"(exports, module2) {
1896
1896
  var once = require_once();
1897
1897
  var eos = require_end_of_stream();
1898
- var fs6;
1898
+ var fs5;
1899
1899
  try {
1900
- fs6 = require("fs");
1900
+ fs5 = require("fs");
1901
1901
  } catch (e) {
1902
1902
  }
1903
1903
  var noop = function() {
@@ -1909,9 +1909,9 @@ var require_pump = __commonJS({
1909
1909
  var isFS = function(stream) {
1910
1910
  if (!ancient)
1911
1911
  return false;
1912
- if (!fs6)
1912
+ if (!fs5)
1913
1913
  return false;
1914
- return (stream instanceof (fs6.ReadStream || noop) || stream instanceof (fs6.WriteStream || noop)) && isFn(stream.close);
1914
+ return (stream instanceof (fs5.ReadStream || noop) || stream instanceof (fs5.WriteStream || noop)) && isFn(stream.close);
1915
1915
  };
1916
1916
  var isRequest = function(stream) {
1917
1917
  return stream.setHeader && isFn(stream.abort);
@@ -2143,7 +2143,7 @@ var require_signal_exit = __commonJS({
2143
2143
  } else {
2144
2144
  assert = require("assert");
2145
2145
  signals = require_signals();
2146
- isWin2 = /^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 (isWin2 && 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 isWin2;
2275
+ var isWin3;
2276
2276
  var EE;
2277
2277
  var emitter;
2278
2278
  var unload;
@@ -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: join6, delimiter, 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}`);
@@ -2680,7 +2680,7 @@ var require_lib = __commonJS({
2680
2680
  var getPathPart = (raw, cmd) => {
2681
2681
  const pathPart = /^".*"$/.test(raw) ? raw.slice(1, -1) : raw;
2682
2682
  const prefix = !pathPart && rRel.test(cmd) ? cmd.slice(0, 2) : "";
2683
- return prefix + join7(pathPart, cmd);
2683
+ return prefix + join6(pathPart, cmd);
2684
2684
  };
2685
2685
  var which3 = async (cmd, opt = {}) => {
2686
2686
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
@@ -2748,20 +2748,147 @@ __export(src_exports, {
2748
2748
  version: () => version
2749
2749
  });
2750
2750
  module.exports = __toCommonJS(src_exports);
2751
- var import_fs6 = __toESM(require("fs"));
2751
+ var import_fs5 = __toESM(require("fs"));
2752
2752
  var import_util = require("util");
2753
- var import_path6 = require("path");
2754
- var import_build_utils8 = require("@vercel/build-utils");
2753
+ var import_path5 = require("path");
2754
+ var import_build_utils7 = require("@vercel/build-utils");
2755
2755
 
2756
2756
  // src/install.ts
2757
2757
  var import_execa = __toESM(require_execa());
2758
- var import_fs = __toESM(require("fs"));
2758
+ var import_fs2 = __toESM(require("fs"));
2759
2759
  var import_os = __toESM(require("os"));
2760
- var import_path = require("path");
2760
+ var import_path2 = require("path");
2761
2761
  var import_which = __toESM(require_lib());
2762
+ var import_build_utils2 = require("@vercel/build-utils");
2763
+
2764
+ // src/utils.ts
2765
+ var import_fs = __toESM(require("fs"));
2766
+ var import_path = require("path");
2762
2767
  var import_build_utils = require("@vercel/build-utils");
2768
+ var execa = require_execa();
2763
2769
  var isWin = process.platform === "win32";
2764
- var uvExec = isWin ? "uv.exe" : "uv";
2770
+ var isInVirtualEnv = () => {
2771
+ return process.env.VIRTUAL_ENV;
2772
+ };
2773
+ function getVenvBinDir(venvPath) {
2774
+ return (0, import_path.join)(venvPath, isWin ? "Scripts" : "bin");
2775
+ }
2776
+ function useVirtualEnv(workPath, env, systemPython) {
2777
+ const venvDirs = [".venv", "venv"];
2778
+ let pythonCmd = systemPython;
2779
+ for (const venv of venvDirs) {
2780
+ const venvRoot = (0, import_path.join)(workPath, venv);
2781
+ const binDir = process.platform === "win32" ? (0, import_path.join)(venvRoot, "Scripts") : (0, import_path.join)(venvRoot, "bin");
2782
+ const candidates = process.platform === "win32" ? [(0, import_path.join)(binDir, "python.exe"), (0, import_path.join)(binDir, "python")] : [(0, import_path.join)(binDir, "python3"), (0, import_path.join)(binDir, "python")];
2783
+ const found = candidates.find((p) => import_fs.default.existsSync(p));
2784
+ if (found) {
2785
+ pythonCmd = found;
2786
+ env.VIRTUAL_ENV = venvRoot;
2787
+ env.PATH = `${binDir}${import_path.delimiter}${env.PATH || ""}`;
2788
+ return { pythonCmd, venvRoot };
2789
+ }
2790
+ }
2791
+ return { pythonCmd };
2792
+ }
2793
+ function createVenvEnv(venvPath, baseEnv = process.env) {
2794
+ const env = { ...baseEnv, VIRTUAL_ENV: venvPath };
2795
+ const binDir = getVenvBinDir(venvPath);
2796
+ const existingPath = env.PATH || process.env.PATH || "";
2797
+ env.PATH = existingPath ? `${binDir}${import_path.delimiter}${existingPath}` : binDir;
2798
+ return env;
2799
+ }
2800
+ async function ensureVenv({
2801
+ pythonPath,
2802
+ venvPath
2803
+ }) {
2804
+ const marker = (0, import_path.join)(venvPath, "pyvenv.cfg");
2805
+ try {
2806
+ await import_fs.default.promises.access(marker);
2807
+ return;
2808
+ } catch {
2809
+ }
2810
+ await import_fs.default.promises.mkdir(venvPath, { recursive: true });
2811
+ console.log(`Creating virtual environment at "${venvPath}"...`);
2812
+ await execa(pythonPath, ["-m", "venv", venvPath]);
2813
+ }
2814
+ function getVenvPythonBin(venvPath) {
2815
+ return (0, import_path.join)(getVenvBinDir(venvPath), isWin ? "python.exe" : "python");
2816
+ }
2817
+ async function runPyprojectScript(workPath, scriptNames, env) {
2818
+ const pyprojectPath = (0, import_path.join)(workPath, "pyproject.toml");
2819
+ if (!import_fs.default.existsSync(pyprojectPath))
2820
+ return false;
2821
+ let pyproject = null;
2822
+ try {
2823
+ pyproject = await (0, import_build_utils.readConfigFile)(pyprojectPath);
2824
+ } catch {
2825
+ console.error("Failed to parse pyproject.toml");
2826
+ return false;
2827
+ }
2828
+ const scripts = pyproject?.tool?.vercel?.scripts || {};
2829
+ const candidates = typeof scriptNames === "string" ? [scriptNames] : Array.from(scriptNames);
2830
+ const scriptToRun = candidates.find((name) => Boolean(scripts[name]));
2831
+ if (!scriptToRun)
2832
+ return false;
2833
+ const systemPython = process.platform === "win32" ? "python" : "python3";
2834
+ const finalEnv = { ...process.env, ...env };
2835
+ useVirtualEnv(workPath, finalEnv, systemPython);
2836
+ const scriptCommand = scripts[scriptToRun];
2837
+ if (typeof scriptCommand === "string" && scriptCommand.trim()) {
2838
+ console.log(`Executing: ${scriptCommand}`);
2839
+ await (0, import_build_utils.execCommand)(scriptCommand, {
2840
+ cwd: workPath,
2841
+ env: finalEnv
2842
+ });
2843
+ return true;
2844
+ }
2845
+ return false;
2846
+ }
2847
+ async function runUvCommand(options) {
2848
+ const { uvPath, args, cwd, venvPath } = options;
2849
+ const pretty = `uv ${args.join(" ")}`;
2850
+ (0, import_build_utils.debug)(`Running "${pretty}"...`);
2851
+ if (!uvPath) {
2852
+ throw new Error(`uv is required to run "${pretty}" but is not available`);
2853
+ }
2854
+ try {
2855
+ await execa(uvPath, args, {
2856
+ cwd,
2857
+ env: createVenvEnv(venvPath)
2858
+ });
2859
+ return true;
2860
+ } catch (err) {
2861
+ const error = new Error(
2862
+ `Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
2863
+ );
2864
+ if (err && typeof err === "object") {
2865
+ if ("code" in err) {
2866
+ error.code = err.code;
2867
+ } else if ("signal" in err) {
2868
+ error.code = err.signal;
2869
+ }
2870
+ }
2871
+ throw error;
2872
+ }
2873
+ }
2874
+ function findDir({
2875
+ file,
2876
+ entryDirectory,
2877
+ workPath,
2878
+ fsFiles
2879
+ }) {
2880
+ if (fsFiles[(0, import_path.join)(entryDirectory, file)]) {
2881
+ return (0, import_path.join)(workPath, entryDirectory);
2882
+ }
2883
+ if (fsFiles[file]) {
2884
+ return workPath;
2885
+ }
2886
+ return null;
2887
+ }
2888
+
2889
+ // src/install.ts
2890
+ var isWin2 = process.platform === "win32";
2891
+ var uvExec = isWin2 ? "uv.exe" : "uv";
2765
2892
  var makeDependencyCheckCode = (dependency) => `
2766
2893
  from importlib import util
2767
2894
  dep = '${dependency}'.replace('-', '_')
@@ -2776,7 +2903,7 @@ async function isInstalled(pythonPath, dependency, cwd) {
2776
2903
  {
2777
2904
  stdio: "pipe",
2778
2905
  cwd,
2779
- env: { ...process.env, PYTHONPATH: (0, import_path.join)(cwd, resolveVendorDir()) }
2906
+ env: { ...process.env, PYTHONPATH: (0, import_path2.join)(cwd, resolveVendorDir()) }
2780
2907
  }
2781
2908
  );
2782
2909
  return stdout.startsWith(cwd);
@@ -2799,7 +2926,7 @@ async function areRequirementsInstalled(pythonPath, requirementsPath, cwd) {
2799
2926
  {
2800
2927
  stdio: "pipe",
2801
2928
  cwd,
2802
- env: { ...process.env, PYTHONPATH: (0, import_path.join)(cwd, resolveVendorDir()) }
2929
+ env: { ...process.env, PYTHONPATH: (0, import_path2.join)(cwd, resolveVendorDir()) }
2803
2930
  }
2804
2931
  );
2805
2932
  return true;
@@ -2807,10 +2934,334 @@ async function areRequirementsInstalled(pythonPath, requirementsPath, cwd) {
2807
2934
  return false;
2808
2935
  }
2809
2936
  }
2937
+ async function runUvSync({
2938
+ uvPath,
2939
+ venvPath,
2940
+ projectDir,
2941
+ locked
2942
+ }) {
2943
+ const args = ["sync", "--active", "--no-dev"];
2944
+ if (locked) {
2945
+ args.push("--locked");
2946
+ }
2947
+ args.push("--no-editable");
2948
+ await runUvCommand({
2949
+ uvPath,
2950
+ args,
2951
+ cwd: projectDir,
2952
+ venvPath
2953
+ });
2954
+ }
2955
+ async function getSitePackagesDirs(pythonBin) {
2956
+ const code = `
2957
+ import json
2958
+ import sysconfig
2959
+ paths = []
2960
+ for key in ("purelib", "platlib"):
2961
+ candidate = sysconfig.get_path(key)
2962
+ if candidate and candidate not in paths:
2963
+ paths.append(candidate)
2964
+ print(json.dumps(paths))
2965
+ `.trim();
2966
+ const { stdout } = await (0, import_execa.default)(pythonBin, ["-c", code]);
2967
+ try {
2968
+ const parsed = JSON.parse(stdout);
2969
+ if (Array.isArray(parsed)) {
2970
+ return parsed.filter((p) => typeof p === "string");
2971
+ }
2972
+ } catch (err) {
2973
+ (0, import_build_utils2.debug)("Failed to parse site-packages output", err);
2974
+ }
2975
+ return [];
2976
+ }
2977
+ async function getVenvSitePackagesDirs(venvPath) {
2978
+ const pythonBin = getVenvPythonBin(venvPath);
2979
+ return getSitePackagesDirs(pythonBin);
2980
+ }
2810
2981
  function resolveVendorDir() {
2811
2982
  const vendorDir = process.env.VERCEL_PYTHON_VENDOR_DIR || "_vendor";
2812
2983
  return vendorDir;
2813
2984
  }
2985
+ async function detectInstallSource({
2986
+ workPath,
2987
+ entryDirectory,
2988
+ fsFiles
2989
+ }) {
2990
+ const uvLockDir = findDir({
2991
+ file: "uv.lock",
2992
+ entryDirectory,
2993
+ workPath,
2994
+ fsFiles
2995
+ });
2996
+ const pyprojectDir = findDir({
2997
+ file: "pyproject.toml",
2998
+ entryDirectory,
2999
+ workPath,
3000
+ fsFiles
3001
+ });
3002
+ const pipfileLockDir = findDir({
3003
+ file: "Pipfile.lock",
3004
+ entryDirectory,
3005
+ workPath,
3006
+ fsFiles
3007
+ });
3008
+ const pipfileDir = findDir({
3009
+ file: "Pipfile",
3010
+ entryDirectory,
3011
+ workPath,
3012
+ fsFiles
3013
+ });
3014
+ const requirementsDir = findDir({
3015
+ file: "requirements.txt",
3016
+ entryDirectory,
3017
+ workPath,
3018
+ fsFiles
3019
+ });
3020
+ let manifestPath = null;
3021
+ let manifestType = null;
3022
+ if (uvLockDir && pyprojectDir) {
3023
+ manifestType = "uv.lock";
3024
+ manifestPath = (0, import_path2.join)(uvLockDir, "uv.lock");
3025
+ } else if (pyprojectDir) {
3026
+ manifestType = "pyproject.toml";
3027
+ manifestPath = (0, import_path2.join)(pyprojectDir, "pyproject.toml");
3028
+ } else if (pipfileLockDir) {
3029
+ manifestType = "Pipfile.lock";
3030
+ manifestPath = (0, import_path2.join)(pipfileLockDir, "Pipfile.lock");
3031
+ } else if (pipfileDir) {
3032
+ manifestType = "Pipfile";
3033
+ manifestPath = (0, import_path2.join)(pipfileDir, "Pipfile");
3034
+ } else if (requirementsDir) {
3035
+ manifestType = "requirements.txt";
3036
+ manifestPath = (0, import_path2.join)(requirementsDir, "requirements.txt");
3037
+ }
3038
+ let manifestContent;
3039
+ if (manifestPath) {
3040
+ try {
3041
+ manifestContent = await import_fs2.default.promises.readFile(manifestPath, "utf8");
3042
+ } catch (err) {
3043
+ (0, import_build_utils2.debug)("Failed to read install manifest contents", err);
3044
+ }
3045
+ }
3046
+ return { manifestPath, manifestType, manifestContent };
3047
+ }
3048
+ async function createPyprojectToml({
3049
+ projectName,
3050
+ pyprojectPath,
3051
+ dependencies
3052
+ }) {
3053
+ const requiresPython = ">=3.12";
3054
+ const depsToml = dependencies.length > 0 ? [
3055
+ "dependencies = [",
3056
+ ...dependencies.map((dep) => ` "${dep}",`),
3057
+ "]"
3058
+ ].join("\n") : "dependencies = []";
3059
+ const content = [
3060
+ "[project]",
3061
+ `name = "${projectName}"`,
3062
+ 'version = "0.1.0"',
3063
+ `requires-python = "${requiresPython}"`,
3064
+ "classifiers = [",
3065
+ ' "Private :: Do Not Upload",',
3066
+ "]",
3067
+ depsToml,
3068
+ ""
3069
+ ].join("\n");
3070
+ await import_fs2.default.promises.writeFile(pyprojectPath, content);
3071
+ }
3072
+ async function uvLock({
3073
+ projectDir,
3074
+ uvPath
3075
+ }) {
3076
+ const args = ["lock"];
3077
+ const pretty = `${uvPath} ${args.join(" ")}`;
3078
+ (0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
3079
+ try {
3080
+ await (0, import_execa.default)(uvPath, args, { cwd: projectDir });
3081
+ } catch (err) {
3082
+ throw new Error(
3083
+ `Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
3084
+ );
3085
+ }
3086
+ }
3087
+ async function uvAddDependencies({
3088
+ projectDir,
3089
+ uvPath,
3090
+ venvPath,
3091
+ dependencies
3092
+ }) {
3093
+ const toAdd = dependencies.filter(Boolean);
3094
+ if (!toAdd.length)
3095
+ return;
3096
+ const args = ["add", "--active", ...toAdd];
3097
+ const pretty = `${uvPath} ${args.join(" ")}`;
3098
+ (0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
3099
+ await runUvCommand({ uvPath, args, cwd: projectDir, venvPath });
3100
+ }
3101
+ async function uvAddFromFile({
3102
+ projectDir,
3103
+ uvPath,
3104
+ venvPath,
3105
+ requirementsPath
3106
+ }) {
3107
+ const args = ["add", "--active", "-r", requirementsPath];
3108
+ const pretty = `${uvPath} ${args.join(" ")}`;
3109
+ (0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
3110
+ await runUvCommand({ uvPath, args, cwd: projectDir, venvPath });
3111
+ }
3112
+ function getDependencyName(spec) {
3113
+ const match = spec.match(/^[A-Za-z0-9_.-]+/);
3114
+ return match ? match[0].toLowerCase() : spec.toLowerCase();
3115
+ }
3116
+ async function filterMissingRuntimeDependencies({
3117
+ pyprojectPath,
3118
+ runtimeDependencies
3119
+ }) {
3120
+ let declared = [];
3121
+ try {
3122
+ const config = await (0, import_build_utils2.readConfigFile)(pyprojectPath);
3123
+ declared = config?.project?.dependencies || [];
3124
+ } catch (err) {
3125
+ (0, import_build_utils2.debug)("Failed to parse pyproject.toml when filtering runtime deps", err);
3126
+ }
3127
+ const declaredNames = new Set(declared.map(getDependencyName));
3128
+ return runtimeDependencies.filter((spec) => {
3129
+ const name = getDependencyName(spec);
3130
+ return !declaredNames.has(name);
3131
+ });
3132
+ }
3133
+ async function ensureUvProject({
3134
+ workPath,
3135
+ entryDirectory,
3136
+ fsFiles,
3137
+ pythonPath,
3138
+ pipPath,
3139
+ uvPath,
3140
+ venvPath,
3141
+ meta,
3142
+ runtimeDependencies
3143
+ }) {
3144
+ const installInfo = await detectInstallSource({
3145
+ workPath,
3146
+ entryDirectory,
3147
+ fsFiles
3148
+ });
3149
+ const { manifestType, manifestPath } = installInfo;
3150
+ let projectDir;
3151
+ let pyprojectPath;
3152
+ if (manifestType === "uv.lock") {
3153
+ if (!manifestPath) {
3154
+ throw new Error("Expected uv.lock path to be resolved, but it was null");
3155
+ }
3156
+ projectDir = (0, import_path2.dirname)(manifestPath);
3157
+ pyprojectPath = (0, import_path2.join)(projectDir, "pyproject.toml");
3158
+ if (!import_fs2.default.existsSync(pyprojectPath)) {
3159
+ throw new Error(
3160
+ `Expected "pyproject.toml" next to "uv.lock" in "${projectDir}"`
3161
+ );
3162
+ }
3163
+ console.log("Installing required dependencies from uv.lock...");
3164
+ } else if (manifestType === "pyproject.toml") {
3165
+ if (!manifestPath) {
3166
+ throw new Error(
3167
+ "Expected pyproject.toml path to be resolved, but it was null"
3168
+ );
3169
+ }
3170
+ projectDir = (0, import_path2.dirname)(manifestPath);
3171
+ pyprojectPath = manifestPath;
3172
+ console.log("Installing required dependencies from pyproject.toml...");
3173
+ const lockPath2 = (0, import_path2.join)(projectDir, "uv.lock");
3174
+ if (!import_fs2.default.existsSync(lockPath2)) {
3175
+ await uvLock({ projectDir, uvPath });
3176
+ }
3177
+ } else if (manifestType === "Pipfile.lock" || manifestType === "Pipfile") {
3178
+ if (!manifestPath) {
3179
+ throw new Error(
3180
+ "Expected Pipfile/Pipfile.lock path to be resolved, but it was null"
3181
+ );
3182
+ }
3183
+ projectDir = (0, import_path2.dirname)(manifestPath);
3184
+ console.log(`Installing required dependencies from ${manifestType}...`);
3185
+ const exportedReq = await exportRequirementsFromPipfile({
3186
+ pythonPath,
3187
+ pipPath,
3188
+ uvPath,
3189
+ projectDir,
3190
+ meta
3191
+ });
3192
+ pyprojectPath = (0, import_path2.join)(projectDir, "pyproject.toml");
3193
+ if (!import_fs2.default.existsSync(pyprojectPath)) {
3194
+ await createPyprojectToml({
3195
+ projectName: "app",
3196
+ pyprojectPath,
3197
+ dependencies: []
3198
+ });
3199
+ }
3200
+ await uvAddFromFile({
3201
+ projectDir,
3202
+ uvPath,
3203
+ venvPath,
3204
+ requirementsPath: exportedReq
3205
+ });
3206
+ } else if (manifestType === "requirements.txt") {
3207
+ if (!manifestPath) {
3208
+ throw new Error(
3209
+ "Expected requirements.txt path to be resolved, but it was null"
3210
+ );
3211
+ }
3212
+ projectDir = (0, import_path2.dirname)(manifestPath);
3213
+ pyprojectPath = (0, import_path2.join)(projectDir, "pyproject.toml");
3214
+ console.log(
3215
+ "Installing required dependencies from requirements.txt with uv..."
3216
+ );
3217
+ if (!import_fs2.default.existsSync(pyprojectPath)) {
3218
+ await createPyprojectToml({
3219
+ projectName: "app",
3220
+ pyprojectPath,
3221
+ dependencies: []
3222
+ });
3223
+ }
3224
+ await uvAddFromFile({
3225
+ projectDir,
3226
+ uvPath,
3227
+ venvPath,
3228
+ requirementsPath: manifestPath
3229
+ });
3230
+ } else {
3231
+ projectDir = workPath;
3232
+ pyprojectPath = (0, import_path2.join)(projectDir, "pyproject.toml");
3233
+ console.log(
3234
+ "No Python manifest found; creating an empty pyproject.toml and uv.lock..."
3235
+ );
3236
+ await createPyprojectToml({
3237
+ projectName: "app",
3238
+ pyprojectPath,
3239
+ dependencies: []
3240
+ });
3241
+ await uvLock({ projectDir, uvPath });
3242
+ }
3243
+ if (runtimeDependencies.length) {
3244
+ const missingRuntimeDeps = await filterMissingRuntimeDependencies({
3245
+ pyprojectPath,
3246
+ runtimeDependencies
3247
+ });
3248
+ if (missingRuntimeDeps.length) {
3249
+ await uvAddDependencies({
3250
+ projectDir,
3251
+ uvPath,
3252
+ venvPath,
3253
+ dependencies: missingRuntimeDeps
3254
+ });
3255
+ }
3256
+ }
3257
+ const lockPath = (0, import_path2.join)(projectDir, "uv.lock");
3258
+ if (!import_fs2.default.existsSync(lockPath)) {
3259
+ throw new Error(
3260
+ `Expected "uv.lock" to exist in "${projectDir}" after preparing uv project`
3261
+ );
3262
+ }
3263
+ return { projectDir, pyprojectPath, lockPath };
3264
+ }
2814
3265
  async function getGlobalScriptsDir(pythonPath) {
2815
3266
  const code = `import sysconfig; print(sysconfig.get_path('scripts'))`;
2816
3267
  try {
@@ -2818,7 +3269,7 @@ async function getGlobalScriptsDir(pythonPath) {
2818
3269
  const out = stdout.trim();
2819
3270
  return out || null;
2820
3271
  } catch (err) {
2821
- (0, import_build_utils.debug)("Failed to resolve Python global scripts directory", err);
3272
+ (0, import_build_utils2.debug)("Failed to resolve Python global scripts directory", err);
2822
3273
  return null;
2823
3274
  }
2824
3275
  }
@@ -2832,12 +3283,12 @@ async function getUserScriptsDir(pythonPath) {
2832
3283
  const out = stdout.trim();
2833
3284
  return out || null;
2834
3285
  } catch (err) {
2835
- (0, import_build_utils.debug)("Failed to resolve Python user scripts directory", err);
3286
+ (0, import_build_utils2.debug)("Failed to resolve Python user scripts directory", err);
2836
3287
  return null;
2837
3288
  }
2838
3289
  }
2839
3290
  async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
2840
- const target = targetDir ? (0, import_path.join)(targetDir, resolveVendorDir()) : resolveVendorDir();
3291
+ const target = targetDir ? (0, import_path2.join)(targetDir, resolveVendorDir()) : resolveVendorDir();
2841
3292
  process.env.PIP_USER = "0";
2842
3293
  if (uvPath) {
2843
3294
  const uvArgs = [
@@ -2847,10 +3298,10 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
2847
3298
  "--no-cache-dir",
2848
3299
  "--target",
2849
3300
  target,
2850
- ...args
3301
+ ...filterUnsafeUvPipArgs(args)
2851
3302
  ];
2852
3303
  const prettyUv = `${uvPath} ${uvArgs.join(" ")}`;
2853
- (0, import_build_utils.debug)(`Running "${prettyUv}"...`);
3304
+ (0, import_build_utils2.debug)(`Running "${prettyUv}"...`);
2854
3305
  try {
2855
3306
  await (0, import_execa.default)(uvPath, uvArgs, {
2856
3307
  cwd: workPath
@@ -2858,7 +3309,7 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
2858
3309
  return;
2859
3310
  } catch (err) {
2860
3311
  console.log(`Failed to run "${prettyUv}", falling back to pip`);
2861
- (0, import_build_utils.debug)(`error: ${err}`);
3312
+ (0, import_build_utils2.debug)(`error: ${err}`);
2862
3313
  }
2863
3314
  }
2864
3315
  const cmdArgs = [
@@ -2871,14 +3322,14 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
2871
3322
  ...args
2872
3323
  ];
2873
3324
  const pretty = `${pipPath} ${cmdArgs.join(" ")}`;
2874
- (0, import_build_utils.debug)(`Running "${pretty}"...`);
3325
+ (0, import_build_utils2.debug)(`Running "${pretty}"...`);
2875
3326
  try {
2876
3327
  await (0, import_execa.default)(pipPath, cmdArgs, {
2877
3328
  cwd: workPath
2878
3329
  });
2879
3330
  } catch (err) {
2880
3331
  console.log(`Failed to run "${pretty}"`);
2881
- (0, import_build_utils.debug)(`error: ${err}`);
3332
+ (0, import_build_utils2.debug)(`error: ${err}`);
2882
3333
  throw err;
2883
3334
  }
2884
3335
  }
@@ -2889,38 +3340,38 @@ async function maybeFindUvBin(pythonPath) {
2889
3340
  try {
2890
3341
  const globalScriptsDir = await getGlobalScriptsDir(pythonPath);
2891
3342
  if (globalScriptsDir) {
2892
- const uvPath = (0, import_path.join)(globalScriptsDir, uvExec);
2893
- if (import_fs.default.existsSync(uvPath))
3343
+ const uvPath = (0, import_path2.join)(globalScriptsDir, uvExec);
3344
+ if (import_fs2.default.existsSync(uvPath))
2894
3345
  return uvPath;
2895
3346
  }
2896
3347
  } catch (err) {
2897
- (0, import_build_utils.debug)("Failed to resolve Python global scripts directory", err);
3348
+ (0, import_build_utils2.debug)("Failed to resolve Python global scripts directory", err);
2898
3349
  }
2899
3350
  try {
2900
3351
  const userScriptsDir = await getUserScriptsDir(pythonPath);
2901
3352
  if (userScriptsDir) {
2902
- const uvPath = (0, import_path.join)(userScriptsDir, uvExec);
2903
- if (import_fs.default.existsSync(uvPath))
3353
+ const uvPath = (0, import_path2.join)(userScriptsDir, uvExec);
3354
+ if (import_fs2.default.existsSync(uvPath))
2904
3355
  return uvPath;
2905
3356
  }
2906
3357
  } catch (err) {
2907
- (0, import_build_utils.debug)("Failed to resolve Python user scripts directory", err);
3358
+ (0, import_build_utils2.debug)("Failed to resolve Python user scripts directory", err);
2908
3359
  }
2909
3360
  try {
2910
3361
  const candidates = [];
2911
- if (!isWin) {
2912
- candidates.push((0, import_path.join)(import_os.default.homedir(), ".local", "bin", "uv"));
3362
+ if (!isWin2) {
3363
+ candidates.push((0, import_path2.join)(import_os.default.homedir(), ".local", "bin", "uv"));
2913
3364
  candidates.push("/usr/local/bin/uv");
2914
3365
  candidates.push("/opt/homebrew/bin/uv");
2915
3366
  } else {
2916
3367
  candidates.push("C:\\Users\\Public\\uv\\uv.exe");
2917
3368
  }
2918
3369
  for (const p of candidates) {
2919
- if (import_fs.default.existsSync(p))
3370
+ if (import_fs2.default.existsSync(p))
2920
3371
  return p;
2921
3372
  }
2922
3373
  } catch (err) {
2923
- (0, import_build_utils.debug)("Failed to resolve uv fallback paths", err);
3374
+ (0, import_build_utils2.debug)("Failed to resolve uv fallback paths", err);
2924
3375
  }
2925
3376
  return null;
2926
3377
  }
@@ -2968,7 +3419,7 @@ async function installRequirement({
2968
3419
  }) {
2969
3420
  const actualTargetDir = targetDir || workPath;
2970
3421
  if (meta.isDev && await isInstalled(pythonPath, dependency, actualTargetDir)) {
2971
- (0, import_build_utils.debug)(
3422
+ (0, import_build_utils2.debug)(
2972
3423
  `Skipping ${dependency} dependency installation, already installed in ${actualTargetDir}`
2973
3424
  );
2974
3425
  return;
@@ -2988,7 +3439,7 @@ async function installRequirementsFile({
2988
3439
  }) {
2989
3440
  const actualTargetDir = targetDir || workPath;
2990
3441
  if (meta.isDev && await areRequirementsInstalled(pythonPath, filePath, actualTargetDir)) {
2991
- (0, import_build_utils.debug)(`Skipping requirements file installation, already installed`);
3442
+ (0, import_build_utils2.debug)(`Skipping requirements file installation, already installed`);
2992
3443
  return;
2993
3444
  }
2994
3445
  await pipInstall(
@@ -2999,35 +3450,8 @@ async function installRequirementsFile({
2999
3450
  targetDir
3000
3451
  );
3001
3452
  }
3002
- async function exportRequirementsFromUv(projectDir, uvPath, options = {}) {
3003
- const { locked = false } = options;
3004
- if (!uvPath) {
3005
- throw new Error("uv is not available to export requirements");
3006
- }
3007
- const args = [
3008
- "export",
3009
- "--no-default-groups",
3010
- "--no-emit-workspace",
3011
- "--no-editable"
3012
- ];
3013
- if (locked) {
3014
- args.push("--frozen");
3015
- }
3016
- (0, import_build_utils.debug)(`Running "${uvPath} ${args.join(" ")}" in ${projectDir}...`);
3017
- let stdout;
3018
- try {
3019
- const { stdout: out } = await (0, import_execa.default)(uvPath, args, { cwd: projectDir });
3020
- stdout = out;
3021
- } catch (err) {
3022
- throw new Error(
3023
- `Failed to run "${uvPath} ${args.join(" ")}": ${err instanceof Error ? err.message : String(err)}`
3024
- );
3025
- }
3026
- const tmpDir = await import_fs.default.promises.mkdtemp((0, import_path.join)(import_os.default.tmpdir(), "vercel-uv-"));
3027
- const outPath = (0, import_path.join)(tmpDir, "requirements.uv.txt");
3028
- await import_fs.default.promises.writeFile(outPath, stdout);
3029
- (0, import_build_utils.debug)(`Exported requirements to ${outPath}`);
3030
- return outPath;
3453
+ function filterUnsafeUvPipArgs(args) {
3454
+ return args.filter((arg) => arg !== "--no-warn-script-location");
3031
3455
  }
3032
3456
  async function exportRequirementsFromPipfile({
3033
3457
  pythonPath,
@@ -3036,8 +3460,8 @@ async function exportRequirementsFromPipfile({
3036
3460
  projectDir,
3037
3461
  meta
3038
3462
  }) {
3039
- const tempDir = await import_fs.default.promises.mkdtemp(
3040
- (0, import_path.join)(import_os.default.tmpdir(), "vercel-pipenv-")
3463
+ const tempDir = await import_fs2.default.promises.mkdtemp(
3464
+ (0, import_path2.join)(import_os.default.tmpdir(), "vercel-pipenv-")
3041
3465
  );
3042
3466
  await installRequirement({
3043
3467
  pythonPath,
@@ -3049,9 +3473,9 @@ async function exportRequirementsFromPipfile({
3049
3473
  args: ["--no-warn-script-location"],
3050
3474
  uvPath
3051
3475
  });
3052
- const tempVendorDir = (0, import_path.join)(tempDir, resolveVendorDir());
3053
- const convertCmd = isWin ? (0, import_path.join)(tempVendorDir, "Scripts", "pipfile2req.exe") : (0, import_path.join)(tempVendorDir, "bin", "pipfile2req");
3054
- (0, import_build_utils.debug)(`Running "${convertCmd}" in ${projectDir}...`);
3476
+ const tempVendorDir = (0, import_path2.join)(tempDir, resolveVendorDir());
3477
+ const convertCmd = isWin2 ? (0, import_path2.join)(tempVendorDir, "Scripts", "pipfile2req.exe") : (0, import_path2.join)(tempVendorDir, "bin", "pipfile2req");
3478
+ (0, import_build_utils2.debug)(`Running "${convertCmd}" in ${projectDir}...`);
3055
3479
  let stdout;
3056
3480
  try {
3057
3481
  const { stdout: out } = await (0, import_execa.default)(convertCmd, [], {
@@ -3064,17 +3488,46 @@ async function exportRequirementsFromPipfile({
3064
3488
  `Failed to run "${convertCmd}": ${err instanceof Error ? err.message : String(err)}`
3065
3489
  );
3066
3490
  }
3067
- const outPath = (0, import_path.join)(tempDir, "requirements.pipenv.txt");
3068
- await import_fs.default.promises.writeFile(outPath, stdout);
3069
- (0, import_build_utils.debug)(`Exported pipfile requirements to ${outPath}`);
3491
+ const outPath = (0, import_path2.join)(tempDir, "requirements.pipenv.txt");
3492
+ await import_fs2.default.promises.writeFile(outPath, stdout);
3493
+ (0, import_build_utils2.debug)(`Exported pipfile requirements to ${outPath}`);
3070
3494
  return outPath;
3071
3495
  }
3496
+ async function mirrorSitePackagesIntoVendor({
3497
+ venvPath,
3498
+ vendorDirName
3499
+ }) {
3500
+ const vendorFiles = {};
3501
+ try {
3502
+ const sitePackageDirs = await getVenvSitePackagesDirs(venvPath);
3503
+ for (const dir of sitePackageDirs) {
3504
+ if (!import_fs2.default.existsSync(dir))
3505
+ continue;
3506
+ const dirFiles = await (0, import_build_utils2.glob)("**", dir);
3507
+ for (const relativePath of Object.keys(dirFiles)) {
3508
+ if (relativePath.endsWith(".pyc") || relativePath.includes("__pycache__")) {
3509
+ continue;
3510
+ }
3511
+ const srcFsPath = (0, import_path2.join)(dir, relativePath);
3512
+ const bundlePath = (0, import_path2.join)(vendorDirName, relativePath).replace(
3513
+ /\\/g,
3514
+ "/"
3515
+ );
3516
+ vendorFiles[bundlePath] = new import_build_utils2.FileFsRef({ fsPath: srcFsPath });
3517
+ }
3518
+ }
3519
+ } catch (err) {
3520
+ console.log("Failed to collect site-packages from virtual environment");
3521
+ throw err;
3522
+ }
3523
+ return vendorFiles;
3524
+ }
3072
3525
 
3073
3526
  // src/index.ts
3074
- var import_build_utils9 = require("@vercel/build-utils");
3527
+ var import_build_utils8 = require("@vercel/build-utils");
3075
3528
 
3076
3529
  // src/version.ts
3077
- var import_build_utils2 = require("@vercel/build-utils");
3530
+ var import_build_utils3 = require("@vercel/build-utils");
3078
3531
  var import_which2 = __toESM(require_lib());
3079
3532
  var allOptions = [
3080
3533
  {
@@ -3125,7 +3578,7 @@ function getLatestPythonVersion({
3125
3578
  }
3126
3579
  const selection = allOptions.find(isInstalled2);
3127
3580
  if (!selection) {
3128
- throw new import_build_utils2.NowBuildError({
3581
+ throw new import_build_utils3.NowBuildError({
3129
3582
  code: "PYTHON_NOT_FOUND",
3130
3583
  link: "http://vercel.link/python-version",
3131
3584
  message: `Unable to find any supported Python versions.`
@@ -3226,7 +3679,7 @@ function getSupportedPythonVersion({
3226
3679
  }
3227
3680
  if (requested) {
3228
3681
  if (isDiscontinued(requested)) {
3229
- throw new import_build_utils2.NowBuildError({
3682
+ throw new import_build_utils3.NowBuildError({
3230
3683
  code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
3231
3684
  link: "http://vercel.link/python-version",
3232
3685
  message: `Python version "${requested.version}" detected in ${source} is discontinued and must be upgraded.`
@@ -3257,7 +3710,7 @@ function getSupportedPythonVersion({
3257
3710
  );
3258
3711
  }
3259
3712
  if (isDiscontinued(selection)) {
3260
- throw new import_build_utils2.NowBuildError({
3713
+ throw new import_build_utils3.NowBuildError({
3261
3714
  code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
3262
3715
  link: "http://vercel.link/python-version",
3263
3716
  message: `Python version "${selection.version}" declared in project configuration is discontinued and must be upgraded.`
@@ -3287,16 +3740,16 @@ var import_path4 = require("path");
3287
3740
  var import_build_utils6 = require("@vercel/build-utils");
3288
3741
 
3289
3742
  // src/entrypoint.ts
3290
- var import_fs2 = __toESM(require("fs"));
3291
- var import_path2 = require("path");
3292
- var import_build_utils3 = require("@vercel/build-utils");
3743
+ var import_fs3 = __toESM(require("fs"));
3744
+ var import_path3 = require("path");
3293
3745
  var import_build_utils4 = require("@vercel/build-utils");
3746
+ var import_build_utils5 = require("@vercel/build-utils");
3294
3747
  var FASTAPI_ENTRYPOINT_FILENAMES = ["app", "index", "server", "main"];
3295
3748
  var FASTAPI_ENTRYPOINT_DIRS = ["", "src", "app", "api"];
3296
3749
  var FASTAPI_CONTENT_REGEX = /(from\s+fastapi\s+import\s+FastAPI|import\s+fastapi|FastAPI\s*\()/;
3297
3750
  var FASTAPI_CANDIDATE_ENTRYPOINTS = FASTAPI_ENTRYPOINT_FILENAMES.flatMap(
3298
3751
  (filename) => FASTAPI_ENTRYPOINT_DIRS.map(
3299
- (dir) => import_path2.posix.join(dir, `${filename}.py`)
3752
+ (dir) => import_path3.posix.join(dir, `${filename}.py`)
3300
3753
  )
3301
3754
  );
3302
3755
  function isFastapiEntrypoint(file) {
@@ -3304,7 +3757,7 @@ function isFastapiEntrypoint(file) {
3304
3757
  const fsPath = file.fsPath;
3305
3758
  if (!fsPath)
3306
3759
  return false;
3307
- const contents = import_fs2.default.readFileSync(fsPath, "utf8");
3760
+ const contents = import_fs3.default.readFileSync(fsPath, "utf8");
3308
3761
  return FASTAPI_CONTENT_REGEX.test(contents);
3309
3762
  } catch {
3310
3763
  return false;
@@ -3315,7 +3768,7 @@ var FLASK_ENTRYPOINT_DIRS = ["", "src", "app", "api"];
3315
3768
  var FLASK_CONTENT_REGEX = /(from\s+flask\s+import\s+Flask|import\s+flask|Flask\s*\()/;
3316
3769
  var FLASK_CANDIDATE_ENTRYPOINTS = FLASK_ENTRYPOINT_FILENAMES.flatMap(
3317
3770
  (filename) => FLASK_ENTRYPOINT_DIRS.map(
3318
- (dir) => import_path2.posix.join(dir, `${filename}.py`)
3771
+ (dir) => import_path3.posix.join(dir, `${filename}.py`)
3319
3772
  )
3320
3773
  );
3321
3774
  function isFlaskEntrypoint(file) {
@@ -3323,7 +3776,7 @@ function isFlaskEntrypoint(file) {
3323
3776
  const fsPath = file.fsPath;
3324
3777
  if (!fsPath)
3325
3778
  return false;
3326
- const contents = import_fs2.default.readFileSync(fsPath, "utf8");
3779
+ const contents = import_fs3.default.readFileSync(fsPath, "utf8");
3327
3780
  return FLASK_CONTENT_REGEX.test(contents);
3328
3781
  } catch {
3329
3782
  return false;
@@ -3332,7 +3785,7 @@ function isFlaskEntrypoint(file) {
3332
3785
  async function detectFlaskEntrypoint(workPath, configuredEntrypoint) {
3333
3786
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
3334
3787
  try {
3335
- const fsFiles = await (0, import_build_utils3.glob)("**", workPath);
3788
+ const fsFiles = await (0, import_build_utils4.glob)("**", workPath);
3336
3789
  if (fsFiles[entry])
3337
3790
  return entry;
3338
3791
  const candidates = FLASK_CANDIDATE_ENTRYPOINTS.filter(
@@ -3342,19 +3795,19 @@ async function detectFlaskEntrypoint(workPath, configuredEntrypoint) {
3342
3795
  const flaskEntrypoint = candidates.find(
3343
3796
  (c) => isFlaskEntrypoint(fsFiles[c])
3344
3797
  ) || candidates[0];
3345
- (0, import_build_utils3.debug)(`Detected Flask entrypoint: ${flaskEntrypoint}`);
3798
+ (0, import_build_utils4.debug)(`Detected Flask entrypoint: ${flaskEntrypoint}`);
3346
3799
  return flaskEntrypoint;
3347
3800
  }
3348
3801
  return null;
3349
3802
  } catch {
3350
- (0, import_build_utils3.debug)("Failed to discover entrypoint for Flask");
3803
+ (0, import_build_utils4.debug)("Failed to discover entrypoint for Flask");
3351
3804
  return null;
3352
3805
  }
3353
3806
  }
3354
3807
  async function detectFastapiEntrypoint(workPath, configuredEntrypoint) {
3355
3808
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
3356
3809
  try {
3357
- const fsFiles = await (0, import_build_utils3.glob)("**", workPath);
3810
+ const fsFiles = await (0, import_build_utils4.glob)("**", workPath);
3358
3811
  if (fsFiles[entry])
3359
3812
  return entry;
3360
3813
  const candidates = FASTAPI_CANDIDATE_ENTRYPOINTS.filter(
@@ -3364,17 +3817,17 @@ async function detectFastapiEntrypoint(workPath, configuredEntrypoint) {
3364
3817
  const fastapiEntrypoint = candidates.find(
3365
3818
  (c) => isFastapiEntrypoint(fsFiles[c])
3366
3819
  ) || candidates[0];
3367
- (0, import_build_utils3.debug)(`Detected FastAPI entrypoint: ${fastapiEntrypoint}`);
3820
+ (0, import_build_utils4.debug)(`Detected FastAPI entrypoint: ${fastapiEntrypoint}`);
3368
3821
  return fastapiEntrypoint;
3369
3822
  }
3370
3823
  return null;
3371
3824
  } catch {
3372
- (0, import_build_utils3.debug)("Failed to discover entrypoint for FastAPI");
3825
+ (0, import_build_utils4.debug)("Failed to discover entrypoint for FastAPI");
3373
3826
  return null;
3374
3827
  }
3375
3828
  }
3376
3829
  async function getPyprojectEntrypoint(workPath) {
3377
- const pyprojectData = await (0, import_build_utils4.readConfigFile)((0, import_path2.join)(workPath, "pyproject.toml"));
3830
+ const pyprojectData = await (0, import_build_utils5.readConfigFile)((0, import_path3.join)(workPath, "pyproject.toml"));
3378
3831
  if (!pyprojectData)
3379
3832
  return null;
3380
3833
  const scripts = pyprojectData.project?.scripts;
@@ -3387,7 +3840,7 @@ async function getPyprojectEntrypoint(workPath) {
3387
3840
  const modulePath = match[1];
3388
3841
  const relPath = modulePath.replace(/\./g, "/");
3389
3842
  try {
3390
- const fsFiles = await (0, import_build_utils3.glob)("**", workPath);
3843
+ const fsFiles = await (0, import_build_utils4.glob)("**", workPath);
3391
3844
  const candidates = [`${relPath}.py`, `${relPath}/__init__.py`];
3392
3845
  for (const candidate of candidates) {
3393
3846
  if (fsFiles[candidate])
@@ -3395,7 +3848,7 @@ async function getPyprojectEntrypoint(workPath) {
3395
3848
  }
3396
3849
  return null;
3397
3850
  } catch {
3398
- (0, import_build_utils3.debug)("Failed to discover Python entrypoint from pyproject.toml");
3851
+ (0, import_build_utils4.debug)("Failed to discover Python entrypoint from pyproject.toml");
3399
3852
  return null;
3400
3853
  }
3401
3854
  }
@@ -3411,61 +3864,6 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint)
3411
3864
  return await getPyprojectEntrypoint(workPath);
3412
3865
  }
3413
3866
 
3414
- // src/utils.ts
3415
- var import_fs3 = __toESM(require("fs"));
3416
- var import_path3 = require("path");
3417
- var import_build_utils5 = require("@vercel/build-utils");
3418
- var isInVirtualEnv = () => {
3419
- return process.env.VIRTUAL_ENV;
3420
- };
3421
- function useVirtualEnv(workPath, env, systemPython) {
3422
- const venvDirs = [".venv", "venv"];
3423
- let pythonCmd = systemPython;
3424
- for (const venv of venvDirs) {
3425
- const venvRoot = (0, import_path3.join)(workPath, venv);
3426
- const binDir = process.platform === "win32" ? (0, import_path3.join)(venvRoot, "Scripts") : (0, import_path3.join)(venvRoot, "bin");
3427
- const candidates = process.platform === "win32" ? [(0, import_path3.join)(binDir, "python.exe"), (0, import_path3.join)(binDir, "python")] : [(0, import_path3.join)(binDir, "python3"), (0, import_path3.join)(binDir, "python")];
3428
- const found = candidates.find((p) => import_fs3.default.existsSync(p));
3429
- if (found) {
3430
- pythonCmd = found;
3431
- env.VIRTUAL_ENV = venvRoot;
3432
- env.PATH = `${binDir}${import_path3.delimiter}${env.PATH || ""}`;
3433
- return { pythonCmd, venvRoot };
3434
- }
3435
- }
3436
- return { pythonCmd };
3437
- }
3438
- async function runPyprojectScript(workPath, scriptNames, env) {
3439
- const pyprojectPath = (0, import_path3.join)(workPath, "pyproject.toml");
3440
- if (!import_fs3.default.existsSync(pyprojectPath))
3441
- return false;
3442
- let pyproject = null;
3443
- try {
3444
- pyproject = await (0, import_build_utils5.readConfigFile)(pyprojectPath);
3445
- } catch {
3446
- console.error("Failed to parse pyproject.toml");
3447
- return false;
3448
- }
3449
- const scripts = pyproject?.tool?.vercel?.scripts || {};
3450
- const candidates = typeof scriptNames === "string" ? [scriptNames] : Array.from(scriptNames);
3451
- const scriptToRun = candidates.find((name) => Boolean(scripts[name]));
3452
- if (!scriptToRun)
3453
- return false;
3454
- const systemPython = process.platform === "win32" ? "python" : "python3";
3455
- const finalEnv = { ...process.env, ...env };
3456
- useVirtualEnv(workPath, finalEnv, systemPython);
3457
- const scriptCommand = scripts[scriptToRun];
3458
- if (typeof scriptCommand === "string" && scriptCommand.trim()) {
3459
- console.log(`Executing: ${scriptCommand}`);
3460
- await (0, import_build_utils5.execCommand)(scriptCommand, {
3461
- cwd: workPath,
3462
- env: finalEnv
3463
- });
3464
- return true;
3465
- }
3466
- return false;
3467
- }
3468
-
3469
3867
  // src/start-dev-server.ts
3470
3868
  function silenceNodeWarnings() {
3471
3869
  const original = process.emitWarning.bind(
@@ -3835,159 +4233,37 @@ If you are using a virtual environment, activate it before running "vercel dev",
3835
4233
  }
3836
4234
  };
3837
4235
 
3838
- // src/uv-workspace.ts
3839
- var import_fs5 = __toESM(require("fs"));
3840
- var import_os2 = __toESM(require("os"));
3841
- var import_path5 = require("path");
3842
- var import_build_utils7 = require("@vercel/build-utils");
3843
- function getDependencyName(spec) {
3844
- const match = spec.match(/^[A-Za-z0-9_.-]+/);
3845
- return match ? match[0] : null;
3846
- }
3847
- async function installUvWorkspaceDependencies({
3848
- repoRootPath,
3849
- pyprojectDir,
3850
- pythonPath,
3851
- pipPath,
3852
- uvPath,
3853
- workPath,
3854
- vendorBaseDir,
3855
- meta
3856
- }) {
3857
- if (!repoRootPath || !pyprojectDir) {
3858
- return;
3859
- }
3860
- let rootPyproject = null;
3861
- try {
3862
- rootPyproject = await (0, import_build_utils7.readConfigFile)(
3863
- (0, import_path5.join)(repoRootPath, "pyproject.toml")
3864
- );
3865
- } catch (err) {
3866
- (0, import_build_utils7.debug)("Failed to parse workspace root pyproject.toml", err);
3867
- }
3868
- const uvTool = rootPyproject?.tool?.uv;
3869
- const workspaceCfg = uvTool?.workspace;
3870
- const sourcesCfg = uvTool?.sources;
3871
- if (!workspaceCfg || !sourcesCfg) {
3872
- return;
3873
- }
3874
- const workspaceSourceNames = new Set(
3875
- Object.entries(sourcesCfg).filter(([, src]) => src && src.workspace).map(([name]) => name)
3876
- );
3877
- if (!workspaceSourceNames.size) {
3878
- return;
3879
- }
3880
- let appPyproject = null;
3881
- try {
3882
- appPyproject = await (0, import_build_utils7.readConfigFile)(
3883
- (0, import_path5.join)(pyprojectDir, "pyproject.toml")
3884
- );
3885
- } catch (err) {
3886
- (0, import_build_utils7.debug)("Failed to parse app pyproject.toml for workspace deps", err);
3887
- }
3888
- const appDeps = appPyproject?.project?.dependencies ?? [];
3889
- const workspaceDepsForApp = /* @__PURE__ */ new Set();
3890
- for (const spec of appDeps) {
3891
- const name = getDependencyName(spec);
3892
- if (name && workspaceSourceNames.has(name)) {
3893
- workspaceDepsForApp.add(name);
3894
- }
3895
- }
3896
- if (!workspaceDepsForApp.size) {
3897
- return;
3898
- }
3899
- const members = workspaceCfg.members ?? [];
3900
- const nameToDir = /* @__PURE__ */ new Map();
3901
- for (const member of members) {
3902
- const memberDir = (0, import_path5.join)(repoRootPath, member);
3903
- let memberPyproject = null;
3904
- try {
3905
- memberPyproject = await (0, import_build_utils7.readConfigFile)(
3906
- (0, import_path5.join)(memberDir, "pyproject.toml")
3907
- );
3908
- } catch (err) {
3909
- (0, import_build_utils7.debug)("Failed to parse workspace member pyproject.toml", err);
3910
- continue;
3911
- }
3912
- const projectName = memberPyproject?.project?.name;
3913
- if (projectName) {
3914
- nameToDir.set(projectName, memberDir);
3915
- }
3916
- }
3917
- const requirementLines = [];
3918
- for (const name of workspaceDepsForApp) {
3919
- const dir = nameToDir.get(name);
3920
- if (!dir) {
3921
- (0, import_build_utils7.debug)(
3922
- `uv workspace dependency "${name}" declared but corresponding member directory not found`
3923
- );
3924
- continue;
3925
- }
3926
- requirementLines.push(dir);
3927
- }
3928
- if (!requirementLines.length) {
3929
- return;
3930
- }
3931
- const tmpDir = await import_fs5.default.promises.mkdtemp(
3932
- (0, import_path5.join)(import_os2.default.tmpdir(), "vercel-uv-workspace-")
3933
- );
3934
- const reqPath = (0, import_path5.join)(tmpDir, "requirements.workspace.txt");
3935
- await import_fs5.default.promises.writeFile(reqPath, requirementLines.join("\n"));
3936
- await installRequirementsFile({
3937
- pythonPath,
3938
- pipPath,
3939
- uvPath,
3940
- filePath: reqPath,
3941
- workPath,
3942
- targetDir: vendorBaseDir,
3943
- meta
3944
- });
3945
- }
3946
-
3947
4236
  // src/index.ts
3948
- var readFile = (0, import_util.promisify)(import_fs6.default.readFile);
3949
- var writeFile = (0, import_util.promisify)(import_fs6.default.writeFile);
4237
+ var readFile = (0, import_util.promisify)(import_fs5.default.readFile);
4238
+ var writeFile = (0, import_util.promisify)(import_fs5.default.writeFile);
3950
4239
  var version = 3;
3951
- function findDir({
3952
- file,
3953
- entryDirectory,
3954
- workPath,
3955
- fsFiles
3956
- }) {
3957
- if (fsFiles[(0, import_path6.join)(entryDirectory, file)]) {
3958
- return (0, import_path6.join)(workPath, entryDirectory);
3959
- }
3960
- if (fsFiles[file]) {
3961
- return workPath;
3962
- }
3963
- return null;
3964
- }
3965
4240
  async function downloadFilesInWorkPath({
3966
4241
  entrypoint,
3967
4242
  workPath,
3968
4243
  files,
3969
4244
  meta = {}
3970
4245
  }) {
3971
- (0, import_build_utils8.debug)("Downloading user files...");
3972
- let downloadedFiles = await (0, import_build_utils8.download)(files, workPath, meta);
4246
+ (0, import_build_utils7.debug)("Downloading user files...");
4247
+ let downloadedFiles = await (0, import_build_utils7.download)(files, workPath, meta);
3973
4248
  if (meta.isDev) {
3974
- const { devCacheDir = (0, import_path6.join)(workPath, ".now", "cache") } = meta;
3975
- const destCache = (0, import_path6.join)(devCacheDir, (0, import_path6.basename)(entrypoint, ".py"));
3976
- await (0, import_build_utils8.download)(downloadedFiles, destCache);
3977
- downloadedFiles = await (0, import_build_utils8.glob)("**", destCache);
4249
+ const { devCacheDir = (0, import_path5.join)(workPath, ".now", "cache") } = meta;
4250
+ const destCache = (0, import_path5.join)(devCacheDir, (0, import_path5.basename)(entrypoint, ".py"));
4251
+ await (0, import_build_utils7.download)(downloadedFiles, destCache);
4252
+ downloadedFiles = await (0, import_build_utils7.glob)("**", destCache);
3978
4253
  workPath = destCache;
3979
4254
  }
3980
4255
  return workPath;
3981
4256
  }
3982
4257
  var build = async ({
3983
4258
  workPath,
3984
- repoRootPath,
3985
4259
  files: originalFiles,
3986
4260
  entrypoint,
3987
4261
  meta = {},
3988
4262
  config
3989
4263
  }) => {
3990
4264
  const framework = config?.framework;
4265
+ let spawnEnv;
4266
+ let projectInstallCommand;
3991
4267
  workPath = await downloadFilesInWorkPath({
3992
4268
  workPath,
3993
4269
  files: originalFiles,
@@ -3996,7 +4272,7 @@ var build = async ({
3996
4272
  });
3997
4273
  try {
3998
4274
  if (meta.isDev) {
3999
- const setupCfg = (0, import_path6.join)(workPath, "setup.cfg");
4275
+ const setupCfg = (0, import_path5.join)(workPath, "setup.cfg");
4000
4276
  await writeFile(setupCfg, "[install]\nprefix=\n");
4001
4277
  }
4002
4278
  } catch (err) {
@@ -4009,8 +4285,8 @@ var build = async ({
4009
4285
  lockfileVersion,
4010
4286
  packageJsonPackageManager,
4011
4287
  turboSupportsCorepackHome
4012
- } = await (0, import_build_utils8.scanParentDirs)(workPath, true);
4013
- const spawnEnv = (0, import_build_utils8.getEnvForPackageManager)({
4288
+ } = await (0, import_build_utils7.scanParentDirs)(workPath, true);
4289
+ spawnEnv = (0, import_build_utils7.getEnvForPackageManager)({
4014
4290
  cliType,
4015
4291
  lockfileVersion,
4016
4292
  packageJsonPackageManager,
@@ -4020,12 +4296,9 @@ var build = async ({
4020
4296
  });
4021
4297
  const installCommand = config?.projectSettings?.installCommand;
4022
4298
  if (typeof installCommand === "string") {
4023
- if (installCommand.trim()) {
4024
- console.log(`Running "install" command: \`${installCommand}\`...`);
4025
- await (0, import_build_utils8.execCommand)(installCommand, {
4026
- env: spawnEnv,
4027
- cwd: workPath
4028
- });
4299
+ const trimmed = installCommand.trim();
4300
+ if (trimmed) {
4301
+ projectInstallCommand = trimmed;
4029
4302
  } else {
4030
4303
  console.log('Skipping "install" command...');
4031
4304
  }
@@ -4034,7 +4307,7 @@ var build = async ({
4034
4307
  config?.buildCommand;
4035
4308
  if (projectBuildCommand) {
4036
4309
  console.log(`Running "${projectBuildCommand}"`);
4037
- await (0, import_build_utils8.execCommand)(projectBuildCommand, {
4310
+ await (0, import_build_utils7.execCommand)(projectBuildCommand, {
4038
4311
  env: spawnEnv,
4039
4312
  cwd: workPath
4040
4313
  });
@@ -4046,7 +4319,7 @@ var build = async ({
4046
4319
  );
4047
4320
  }
4048
4321
  }
4049
- let fsFiles = await (0, import_build_utils8.glob)("**", workPath);
4322
+ let fsFiles = await (0, import_build_utils7.glob)("**", workPath);
4050
4323
  if ((framework === "fastapi" || framework === "flask") && (!fsFiles[entrypoint] || !entrypoint.endsWith(".py"))) {
4051
4324
  const detected = await detectPythonEntrypoint(
4052
4325
  config.framework,
@@ -4054,13 +4327,13 @@ var build = async ({
4054
4327
  entrypoint
4055
4328
  );
4056
4329
  if (detected) {
4057
- (0, import_build_utils8.debug)(
4330
+ (0, import_build_utils7.debug)(
4058
4331
  `Resolved Python entrypoint to "${detected}" (configured "${entrypoint}" not found).`
4059
4332
  );
4060
4333
  entrypoint = detected;
4061
4334
  } else {
4062
4335
  const searchedList = framework === "fastapi" ? FASTAPI_CANDIDATE_ENTRYPOINTS.join(", ") : FLASK_CANDIDATE_ENTRYPOINTS.join(", ");
4063
- throw new import_build_utils8.NowBuildError({
4336
+ throw new import_build_utils7.NowBuildError({
4064
4337
  code: `${framework.toUpperCase()}_ENTRYPOINT_NOT_FOUND`,
4065
4338
  message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searchedList}.`,
4066
4339
  link: `https://vercel.com/docs/frameworks/backend/${framework}#exporting-the-${framework}-application`,
@@ -4068,46 +4341,42 @@ var build = async ({
4068
4341
  });
4069
4342
  }
4070
4343
  }
4071
- const entryDirectory = (0, import_path6.dirname)(entrypoint);
4072
- const hasReqLocal = !!fsFiles[(0, import_path6.join)(entryDirectory, "requirements.txt")];
4073
- const hasReqGlobal = !!fsFiles["requirements.txt"];
4074
- const uvLockDir = findDir({
4075
- file: "uv.lock",
4344
+ const entryDirectory = (0, import_path5.dirname)(entrypoint);
4345
+ const pyprojectDir = findDir({
4346
+ file: "pyproject.toml",
4076
4347
  entryDirectory,
4077
4348
  workPath,
4078
4349
  fsFiles
4079
4350
  });
4080
- const pyprojectDir = findDir({
4081
- file: "pyproject.toml",
4351
+ const pipfileLockDir = findDir({
4352
+ file: "Pipfile.lock",
4082
4353
  entryDirectory,
4083
4354
  workPath,
4084
4355
  fsFiles
4085
4356
  });
4086
- const pipfileLockDir = fsFiles[(0, import_path6.join)(entryDirectory, "Pipfile.lock")] ? (0, import_path6.join)(workPath, entryDirectory) : fsFiles["Pipfile.lock"] ? workPath : null;
4087
- const pipfileDir = fsFiles[(0, import_path6.join)(entryDirectory, "Pipfile")] ? (0, import_path6.join)(workPath, entryDirectory) : fsFiles["Pipfile"] ? workPath : null;
4088
4357
  let declaredPythonVersion;
4089
4358
  if (pyprojectDir) {
4090
4359
  let requiresPython;
4091
4360
  try {
4092
- const pyproject = await (0, import_build_utils9.readConfigFile)((0, import_path6.join)(pyprojectDir, "pyproject.toml"));
4361
+ const pyproject = await (0, import_build_utils8.readConfigFile)((0, import_path5.join)(pyprojectDir, "pyproject.toml"));
4093
4362
  requiresPython = pyproject?.project?.["requires-python"];
4094
4363
  } catch (err) {
4095
- (0, import_build_utils8.debug)("Failed to parse pyproject.toml", err);
4364
+ (0, import_build_utils7.debug)("Failed to parse pyproject.toml", err);
4096
4365
  }
4097
4366
  if (typeof requiresPython === "string" && requiresPython.trim()) {
4098
4367
  declaredPythonVersion = {
4099
4368
  version: requiresPython.trim(),
4100
4369
  source: "pyproject.toml"
4101
4370
  };
4102
- (0, import_build_utils8.debug)(`Found requires-python "${requiresPython}" in pyproject.toml`);
4371
+ (0, import_build_utils7.debug)(`Found requires-python "${requiresPython}" in pyproject.toml`);
4103
4372
  }
4104
4373
  } else if (pipfileLockDir) {
4105
4374
  let lock = {};
4106
4375
  try {
4107
- const json = await readFile((0, import_path6.join)(pipfileLockDir, "Pipfile.lock"), "utf8");
4376
+ const json = await readFile((0, import_path5.join)(pipfileLockDir, "Pipfile.lock"), "utf8");
4108
4377
  lock = JSON.parse(json);
4109
4378
  } catch (err) {
4110
- throw new import_build_utils8.NowBuildError({
4379
+ throw new import_build_utils7.NowBuildError({
4111
4380
  code: "INVALID_PIPFILE_LOCK",
4112
4381
  message: "Unable to parse Pipfile.lock"
4113
4382
  });
@@ -4115,191 +4384,68 @@ var build = async ({
4115
4384
  const pyFromLock = lock?._meta?.requires?.python_version;
4116
4385
  if (pyFromLock) {
4117
4386
  declaredPythonVersion = { version: pyFromLock, source: "Pipfile.lock" };
4118
- (0, import_build_utils8.debug)(`Found Python version ${pyFromLock} in Pipfile.lock`);
4387
+ (0, import_build_utils7.debug)(`Found Python version ${pyFromLock} in Pipfile.lock`);
4119
4388
  }
4120
4389
  }
4121
4390
  const pythonVersion = getSupportedPythonVersion({
4122
4391
  isDev: meta.isDev,
4123
4392
  declaredPythonVersion
4124
4393
  });
4125
- fsFiles = await (0, import_build_utils8.glob)("**", workPath);
4126
- const requirementsTxt = (0, import_path6.join)(entryDirectory, "requirements.txt");
4127
- const vendorBaseDir = (0, import_path6.join)(
4128
- workPath,
4129
- ".vercel",
4130
- "python",
4131
- `py${pythonVersion.version}`,
4132
- entryDirectory
4133
- );
4134
- try {
4135
- await import_fs6.default.promises.mkdir(vendorBaseDir, { recursive: true });
4136
- } catch (err) {
4137
- console.log("Failed to create vendor cache directory");
4138
- throw err;
4139
- }
4140
- let installationSource;
4141
- if (uvLockDir && pyprojectDir) {
4142
- installationSource = "uv.lock";
4143
- } else if (pyprojectDir) {
4144
- installationSource = "pyproject.toml";
4145
- } else if (pipfileLockDir) {
4146
- installationSource = "Pipfile.lock";
4147
- } else if (pipfileDir) {
4148
- installationSource = "Pipfile";
4149
- } else if (fsFiles[requirementsTxt] || fsFiles["requirements.txt"]) {
4150
- installationSource = "requirements.txt";
4151
- }
4152
- if (installationSource) {
4153
- console.log(
4154
- `Installing required dependencies from ${installationSource}...`
4155
- );
4156
- } else {
4157
- console.log("Installing required dependencies...");
4158
- }
4159
- let uvPath = null;
4160
- try {
4161
- uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
4162
- console.log(`Using uv at "${uvPath}"`);
4163
- } catch (err) {
4164
- if (uvLockDir || pyprojectDir && !hasReqLocal && !hasReqGlobal) {
4165
- console.log("Failed to install uv");
4166
- throw new Error(
4167
- `uv is required for this project but failed to install: ${err instanceof Error ? err.message : String(err)}`
4168
- );
4169
- }
4170
- (0, import_build_utils8.debug)("Failed to install uv", err);
4171
- }
4172
- await installRequirement({
4394
+ fsFiles = await (0, import_build_utils7.glob)("**", workPath);
4395
+ const venvPath = (0, import_path5.join)(workPath, ".vercel", "python", ".venv");
4396
+ await ensureVenv({
4173
4397
  pythonPath: pythonVersion.pythonPath,
4174
- pipPath: pythonVersion.pipPath,
4175
- uvPath,
4176
- dependency: "werkzeug",
4177
- version: "1.0.1",
4178
- workPath,
4179
- targetDir: vendorBaseDir,
4180
- meta
4398
+ venvPath
4181
4399
  });
4182
- if (framework !== "flask") {
4183
- await installRequirement({
4184
- pythonPath: pythonVersion.pythonPath,
4185
- pipPath: pythonVersion.pipPath,
4186
- uvPath,
4187
- dependency: "uvicorn",
4188
- version: "0.38.0",
4189
- workPath,
4190
- targetDir: vendorBaseDir,
4191
- meta
4400
+ const hasCustomInstallCommand = (framework === "fastapi" || framework === "flask") && !!projectInstallCommand;
4401
+ if (hasCustomInstallCommand) {
4402
+ const baseEnv = spawnEnv || process.env;
4403
+ const pythonEnv = createVenvEnv(venvPath, baseEnv);
4404
+ pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
4405
+ const installCommand = projectInstallCommand;
4406
+ console.log(`Running "install" command: \`${installCommand}\`...`);
4407
+ await (0, import_build_utils7.execCommand)(installCommand, {
4408
+ env: pythonEnv,
4409
+ cwd: workPath
4192
4410
  });
4193
- }
4194
- let installedFromProjectFiles = false;
4195
- if (uvLockDir) {
4196
- (0, import_build_utils8.debug)('Found "uv.lock"');
4197
- if (pyprojectDir) {
4198
- const exportedReq = await exportRequirementsFromUv(pyprojectDir, uvPath, {
4199
- locked: true
4200
- });
4201
- await installRequirementsFile({
4202
- pythonPath: pythonVersion.pythonPath,
4203
- pipPath: pythonVersion.pipPath,
4204
- uvPath,
4205
- filePath: exportedReq,
4206
- workPath,
4207
- targetDir: vendorBaseDir,
4208
- meta
4209
- });
4210
- installedFromProjectFiles = true;
4211
- } else {
4212
- (0, import_build_utils8.debug)('Skipping uv export because "pyproject.toml" was not found');
4213
- }
4214
- } else if (pyprojectDir) {
4215
- (0, import_build_utils8.debug)('Found "pyproject.toml"');
4216
- if (hasReqLocal || hasReqGlobal) {
4217
- console.log(
4218
- "Detected both pyproject.toml and requirements.txt but no lockfile; using pyproject.toml"
4411
+ } else {
4412
+ let uvPath;
4413
+ try {
4414
+ uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
4415
+ console.log(`Using uv at "${uvPath}"`);
4416
+ } catch (err) {
4417
+ console.log("Failed to install or locate uv");
4418
+ throw new Error(
4419
+ `uv is required for this project but failed to install: ${err instanceof Error ? err.message : String(err)}`
4219
4420
  );
4220
4421
  }
4221
- const exportedReq = await exportRequirementsFromUv(pyprojectDir, uvPath, {
4222
- locked: false
4223
- });
4224
- await installRequirementsFile({
4225
- pythonPath: pythonVersion.pythonPath,
4226
- pipPath: pythonVersion.pipPath,
4227
- uvPath,
4228
- filePath: exportedReq,
4229
- workPath,
4230
- targetDir: vendorBaseDir,
4231
- meta
4232
- });
4233
- installedFromProjectFiles = true;
4234
- } else if (pipfileLockDir || pipfileDir) {
4235
- (0, import_build_utils8.debug)(`Found ${pipfileLockDir ? '"Pipfile.lock"' : '"Pipfile"'}`);
4236
- if (hasReqLocal || hasReqGlobal) {
4237
- (0, import_build_utils8.debug)('Skipping Pipfile export because "requirements.txt" exists');
4238
- } else {
4239
- const exportedReq = await exportRequirementsFromPipfile({
4240
- pythonPath: pythonVersion.pythonPath,
4241
- pipPath: pythonVersion.pipPath,
4242
- uvPath,
4243
- projectDir: pipfileLockDir || pipfileDir,
4244
- meta
4245
- });
4246
- await installRequirementsFile({
4247
- pythonPath: pythonVersion.pythonPath,
4248
- pipPath: pythonVersion.pipPath,
4249
- uvPath,
4250
- filePath: exportedReq,
4251
- workPath,
4252
- targetDir: vendorBaseDir,
4253
- meta
4254
- });
4255
- installedFromProjectFiles = true;
4256
- }
4257
- }
4258
- if (!installedFromProjectFiles && fsFiles[requirementsTxt]) {
4259
- (0, import_build_utils8.debug)('Found local "requirements.txt"');
4260
- const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
4261
- await installRequirementsFile({
4262
- pythonPath: pythonVersion.pythonPath,
4263
- pipPath: pythonVersion.pipPath,
4264
- uvPath,
4265
- filePath: requirementsTxtPath,
4422
+ const runtimeDependencies = framework === "flask" ? ["werkzeug>=1.0.1"] : ["werkzeug>=1.0.1", "uvicorn>=0.24"];
4423
+ const { projectDir } = await ensureUvProject({
4266
4424
  workPath,
4267
- targetDir: vendorBaseDir,
4268
- meta
4269
- });
4270
- } else if (!installedFromProjectFiles && fsFiles["requirements.txt"]) {
4271
- (0, import_build_utils8.debug)('Found global "requirements.txt"');
4272
- const requirementsTxtPath = fsFiles["requirements.txt"].fsPath;
4273
- await installRequirementsFile({
4425
+ entryDirectory,
4426
+ fsFiles,
4274
4427
  pythonPath: pythonVersion.pythonPath,
4275
4428
  pipPath: pythonVersion.pipPath,
4276
4429
  uvPath,
4277
- filePath: requirementsTxtPath,
4278
- workPath,
4279
- targetDir: vendorBaseDir,
4280
- meta
4430
+ venvPath,
4431
+ meta,
4432
+ runtimeDependencies
4281
4433
  });
4282
- }
4283
- if (pyprojectDir && repoRootPath) {
4284
- await installUvWorkspaceDependencies({
4285
- repoRootPath,
4286
- pyprojectDir,
4287
- pythonPath: pythonVersion.pythonPath,
4288
- pipPath: pythonVersion.pipPath,
4434
+ await runUvSync({
4289
4435
  uvPath,
4290
- workPath,
4291
- vendorBaseDir,
4292
- meta
4436
+ venvPath,
4437
+ projectDir,
4438
+ locked: true
4293
4439
  });
4294
4440
  }
4295
- const originalPyPath = (0, import_path6.join)(__dirname, "..", "vc_init.py");
4441
+ const originalPyPath = (0, import_path5.join)(__dirname, "..", "vc_init.py");
4296
4442
  const originalHandlerPyContents = await readFile(originalPyPath, "utf8");
4297
- (0, import_build_utils8.debug)("Entrypoint is", entrypoint);
4443
+ (0, import_build_utils7.debug)("Entrypoint is", entrypoint);
4298
4444
  const moduleName = entrypoint.replace(/\//g, ".").replace(/\.py$/i, "");
4299
4445
  const vendorDir = resolveVendorDir();
4300
4446
  const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
4301
4447
  const entrypointWithSuffix = `${entrypoint}${suffix}`;
4302
- (0, import_build_utils8.debug)("Entrypoint with suffix is", entrypointWithSuffix);
4448
+ (0, import_build_utils7.debug)("Entrypoint with suffix is", entrypointWithSuffix);
4303
4449
  const handlerPyContents = originalHandlerPyContents.replace(/__VC_HANDLER_MODULE_NAME/g, moduleName).replace(/__VC_HANDLER_ENTRYPOINT/g, entrypointWithSuffix).replace(/__VC_HANDLER_VENDOR_DIR/g, vendorDir);
4304
4450
  const predefinedExcludes = [
4305
4451
  ".git/**",
@@ -4325,26 +4471,21 @@ var build = async ({
4325
4471
  cwd: workPath,
4326
4472
  ignore: config && typeof config.excludeFiles === "string" ? [...predefinedExcludes, config.excludeFiles] : predefinedExcludes
4327
4473
  };
4328
- const files = await (0, import_build_utils8.glob)("**", globOptions);
4329
- try {
4330
- const cachedVendorAbs = (0, import_path6.join)(vendorBaseDir, resolveVendorDir());
4331
- if (import_fs6.default.existsSync(cachedVendorAbs)) {
4332
- const vendorFiles = await (0, import_build_utils8.glob)("**", cachedVendorAbs, resolveVendorDir());
4333
- for (const [p, f] of Object.entries(vendorFiles)) {
4334
- files[p] = f;
4335
- }
4336
- }
4337
- } catch (err) {
4338
- console.log("Failed to include cached vendor directory");
4339
- throw err;
4474
+ const files = await (0, import_build_utils7.glob)("**", globOptions);
4475
+ const vendorFiles = await mirrorSitePackagesIntoVendor({
4476
+ venvPath,
4477
+ vendorDirName: vendorDir
4478
+ });
4479
+ for (const [p, f] of Object.entries(vendorFiles)) {
4480
+ files[p] = f;
4340
4481
  }
4341
4482
  const handlerPyFilename = "vc__handler__python";
4342
- files[`${handlerPyFilename}.py`] = new import_build_utils8.FileBlob({ data: handlerPyContents });
4483
+ files[`${handlerPyFilename}.py`] = new import_build_utils7.FileBlob({ data: handlerPyContents });
4343
4484
  if (config.framework === "fasthtml") {
4344
4485
  const { SESSKEY = "" } = process.env;
4345
- files[".sesskey"] = new import_build_utils8.FileBlob({ data: `"${SESSKEY}"` });
4486
+ files[".sesskey"] = new import_build_utils7.FileBlob({ data: `"${SESSKEY}"` });
4346
4487
  }
4347
- const output = new import_build_utils8.Lambda({
4488
+ const output = new import_build_utils7.Lambda({
4348
4489
  files,
4349
4490
  handler: `${handlerPyFilename}.vc_handler`,
4350
4491
  runtime: pythonVersion.runtime,
@@ -4380,7 +4521,7 @@ var defaultShouldServe = ({
4380
4521
  if (entrypoint === requestPath && hasProp(files, entrypoint)) {
4381
4522
  return true;
4382
4523
  }
4383
- const { dir, name } = (0, import_path6.parse)(entrypoint);
4524
+ const { dir, name } = (0, import_path5.parse)(entrypoint);
4384
4525
  if (name === "index" && dir === requestPath && hasProp(files, entrypoint)) {
4385
4526
  return true;
4386
4527
  }