@vercel/python 6.3.2 → 6.4.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.
- package/dist/index.js +803 -689
- package/package.json +3 -3
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
91
|
+
var fs5 = require("fs");
|
|
92
92
|
function isexe(path, options, cb) {
|
|
93
|
-
|
|
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(
|
|
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
|
|
122
|
+
var fs5 = require("fs");
|
|
123
123
|
var core;
|
|
124
124
|
if (process.platform === "win32" || global.TESTING_WINDOWS) {
|
|
125
125
|
core = require_windows();
|
|
@@ -174,8 +174,8 @@ var require_isexe = __commonJS({
|
|
|
174
174
|
// ../../node_modules/.pnpm/which@1.3.1/node_modules/which/which.js
|
|
175
175
|
var require_which = __commonJS({
|
|
176
176
|
"../../node_modules/.pnpm/which@1.3.1/node_modules/which/which.js"(exports, module2) {
|
|
177
|
-
module2.exports =
|
|
178
|
-
|
|
177
|
+
module2.exports = which2;
|
|
178
|
+
which2.sync = whichSync;
|
|
179
179
|
var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
180
180
|
var path = require("path");
|
|
181
181
|
var COLON = isWindows ? ";" : ":";
|
|
@@ -206,7 +206,7 @@ var require_which = __commonJS({
|
|
|
206
206
|
extExe: pathExtExe
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
-
function
|
|
209
|
+
function which2(cmd, opt, cb) {
|
|
210
210
|
if (typeof opt === "function") {
|
|
211
211
|
cb = opt;
|
|
212
212
|
opt = {};
|
|
@@ -307,7 +307,7 @@ var require_resolveCommand = __commonJS({
|
|
|
307
307
|
"../../node_modules/.pnpm/cross-spawn@6.0.5/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module2) {
|
|
308
308
|
"use strict";
|
|
309
309
|
var path = require("path");
|
|
310
|
-
var
|
|
310
|
+
var which2 = require_which();
|
|
311
311
|
var pathKey = require_path_key()();
|
|
312
312
|
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
313
313
|
const cwd = process.cwd();
|
|
@@ -320,7 +320,7 @@ var require_resolveCommand = __commonJS({
|
|
|
320
320
|
}
|
|
321
321
|
let resolved;
|
|
322
322
|
try {
|
|
323
|
-
resolved =
|
|
323
|
+
resolved = which2.sync(parsed.command, {
|
|
324
324
|
path: (parsed.options.env || process.env)[pathKey],
|
|
325
325
|
pathExt: withoutPathExt ? path.delimiter : void 0
|
|
326
326
|
});
|
|
@@ -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
|
|
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 =
|
|
412
|
-
|
|
413
|
-
|
|
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
|
|
1501
|
+
var isWin4 = 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 (!
|
|
1516
|
+
if (!isWin4) {
|
|
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 (
|
|
1538
|
+
if (isWin4) {
|
|
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
|
|
1581
|
+
var isWin4 = 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 (!
|
|
1592
|
+
if (!isWin4) {
|
|
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 (
|
|
1607
|
+
if (isWin4 && 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 (
|
|
1613
|
+
if (isWin4 && 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
|
|
1898
|
+
var fs5;
|
|
1899
1899
|
try {
|
|
1900
|
-
|
|
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 (!
|
|
1912
|
+
if (!fs5)
|
|
1913
1913
|
return false;
|
|
1914
|
-
return (stream instanceof (
|
|
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
|
-
|
|
2146
|
+
isWin4 = /^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 (
|
|
2218
|
+
if (isWin4 && 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
|
|
2275
|
+
var isWin4;
|
|
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:
|
|
2654
|
+
var { join: join7, 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,9 +2680,9 @@ 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 +
|
|
2683
|
+
return prefix + join7(pathPart, cmd);
|
|
2684
2684
|
};
|
|
2685
|
-
var
|
|
2685
|
+
var which2 = async (cmd, opt = {}) => {
|
|
2686
2686
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
2687
2687
|
const found = [];
|
|
2688
2688
|
for (const envPart of pathEnv) {
|
|
@@ -2730,8 +2730,8 @@ var require_lib = __commonJS({
|
|
|
2730
2730
|
}
|
|
2731
2731
|
throw getNotFoundError(cmd);
|
|
2732
2732
|
};
|
|
2733
|
-
module2.exports =
|
|
2734
|
-
|
|
2733
|
+
module2.exports = which2;
|
|
2734
|
+
which2.sync = whichSync;
|
|
2735
2735
|
}
|
|
2736
2736
|
});
|
|
2737
2737
|
|
|
@@ -2748,55 +2748,287 @@ __export(src_exports, {
|
|
|
2748
2748
|
version: () => version
|
|
2749
2749
|
});
|
|
2750
2750
|
module.exports = __toCommonJS(src_exports);
|
|
2751
|
-
var
|
|
2751
|
+
var import_fs5 = __toESM(require("fs"));
|
|
2752
2752
|
var import_util = require("util");
|
|
2753
|
-
var
|
|
2754
|
-
var
|
|
2753
|
+
var import_path7 = require("path");
|
|
2754
|
+
var import_build_utils8 = require("@vercel/build-utils");
|
|
2755
2755
|
|
|
2756
2756
|
// src/install.ts
|
|
2757
|
-
var
|
|
2757
|
+
var import_execa3 = __toESM(require_execa());
|
|
2758
|
+
var import_fs3 = __toESM(require("fs"));
|
|
2759
|
+
var import_os2 = __toESM(require("os"));
|
|
2760
|
+
var import_path4 = require("path");
|
|
2761
|
+
var import_build_utils4 = require("@vercel/build-utils");
|
|
2762
|
+
|
|
2763
|
+
// src/utils.ts
|
|
2758
2764
|
var import_fs2 = __toESM(require("fs"));
|
|
2759
|
-
var
|
|
2760
|
-
var import_path2 = require("path");
|
|
2761
|
-
var import_which = __toESM(require_lib());
|
|
2765
|
+
var import_path3 = require("path");
|
|
2762
2766
|
var import_build_utils2 = require("@vercel/build-utils");
|
|
2767
|
+
var import_execa2 = __toESM(require_execa());
|
|
2763
2768
|
|
|
2764
|
-
// src/
|
|
2765
|
-
var
|
|
2769
|
+
// src/uv.ts
|
|
2770
|
+
var import_child_process = require("child_process");
|
|
2766
2771
|
var import_path = require("path");
|
|
2767
|
-
var
|
|
2772
|
+
var import_path2 = require("path");
|
|
2768
2773
|
var import_execa = __toESM(require_execa());
|
|
2769
|
-
var
|
|
2774
|
+
var import_fs = __toESM(require("fs"));
|
|
2775
|
+
var import_os = __toESM(require("os"));
|
|
2776
|
+
var import_which = __toESM(require_lib());
|
|
2777
|
+
var import_build_utils = require("@vercel/build-utils");
|
|
2778
|
+
var UV_VERSION = "0.9.22";
|
|
2779
|
+
var UV_PYTHON_PATH_PREFIX = "/uv/python/";
|
|
2770
2780
|
var UV_PYTHON_DOWNLOADS_MODE = "automatic";
|
|
2781
|
+
var isWin = process.platform === "win32";
|
|
2782
|
+
var uvExec = isWin ? "uv.exe" : "uv";
|
|
2783
|
+
function findUvInPath() {
|
|
2784
|
+
return import_which.default.sync("uv", { nothrow: true });
|
|
2785
|
+
}
|
|
2786
|
+
var UvRunner = class {
|
|
2787
|
+
constructor(uvPath) {
|
|
2788
|
+
this.uvPath = uvPath;
|
|
2789
|
+
}
|
|
2790
|
+
getPath() {
|
|
2791
|
+
return this.uvPath;
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* List installed Python versions managed by uv.
|
|
2795
|
+
* Excludes system Python.
|
|
2796
|
+
*/
|
|
2797
|
+
listInstalledPythons() {
|
|
2798
|
+
let output;
|
|
2799
|
+
try {
|
|
2800
|
+
output = (0, import_child_process.execSync)(
|
|
2801
|
+
`${this.uvPath} python list --only-installed --output-format json`,
|
|
2802
|
+
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
|
|
2803
|
+
);
|
|
2804
|
+
} catch (err) {
|
|
2805
|
+
throw new Error(
|
|
2806
|
+
`Failed to run 'uv python list': ${err instanceof Error ? err.message : String(err)}`
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
if (!output || output.trim() === "" || output.trim() === "[]") {
|
|
2810
|
+
return /* @__PURE__ */ new Set();
|
|
2811
|
+
}
|
|
2812
|
+
let pyList;
|
|
2813
|
+
try {
|
|
2814
|
+
pyList = JSON.parse(output);
|
|
2815
|
+
} catch (err) {
|
|
2816
|
+
throw new Error(
|
|
2817
|
+
`Failed to parse 'uv python list' output: ${err instanceof Error ? err.message : String(err)}`
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
return new Set(
|
|
2821
|
+
pyList.filter(
|
|
2822
|
+
(entry) => entry.path !== null && entry.path.startsWith(UV_PYTHON_PATH_PREFIX) && entry.implementation === "cpython"
|
|
2823
|
+
).map(
|
|
2824
|
+
(entry) => `${entry.version_parts.major}.${entry.version_parts.minor}`
|
|
2825
|
+
)
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2828
|
+
async sync(options) {
|
|
2829
|
+
const { venvPath, projectDir, locked } = options;
|
|
2830
|
+
const args = ["sync", "--active", "--no-dev", "--link-mode", "copy"];
|
|
2831
|
+
if (locked) {
|
|
2832
|
+
args.push("--locked");
|
|
2833
|
+
}
|
|
2834
|
+
args.push("--no-editable");
|
|
2835
|
+
await this.runUvCmd(args, projectDir, venvPath);
|
|
2836
|
+
}
|
|
2837
|
+
async lock(projectDir) {
|
|
2838
|
+
const args = ["lock"];
|
|
2839
|
+
const pretty = `uv ${args.join(" ")}`;
|
|
2840
|
+
(0, import_build_utils.debug)(`Running "${pretty}" in ${projectDir}...`);
|
|
2841
|
+
try {
|
|
2842
|
+
await (0, import_execa.default)(this.uvPath, args, {
|
|
2843
|
+
cwd: projectDir,
|
|
2844
|
+
env: getProtectedUvEnv(process.env)
|
|
2845
|
+
});
|
|
2846
|
+
} catch (err) {
|
|
2847
|
+
throw new Error(
|
|
2848
|
+
`Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
async addDependencies(options) {
|
|
2853
|
+
const { venvPath, projectDir, dependencies } = options;
|
|
2854
|
+
const toAdd = dependencies.filter(Boolean);
|
|
2855
|
+
if (!toAdd.length)
|
|
2856
|
+
return;
|
|
2857
|
+
const args = ["add", "--active", ...toAdd];
|
|
2858
|
+
(0, import_build_utils.debug)(`Running "uv ${args.join(" ")}" in ${projectDir}...`);
|
|
2859
|
+
await this.runUvCmd(args, projectDir, venvPath);
|
|
2860
|
+
}
|
|
2861
|
+
async addFromFile(options) {
|
|
2862
|
+
const { venvPath, projectDir, requirementsPath } = options;
|
|
2863
|
+
const args = ["add", "--active", "-r", requirementsPath];
|
|
2864
|
+
(0, import_build_utils.debug)(`Running "uv ${args.join(" ")}" in ${projectDir}...`);
|
|
2865
|
+
await this.runUvCmd(args, projectDir, venvPath);
|
|
2866
|
+
}
|
|
2867
|
+
async runUvCmd(args, cwd, venvPath) {
|
|
2868
|
+
const pretty = `uv ${args.join(" ")}`;
|
|
2869
|
+
(0, import_build_utils.debug)(`Running "${pretty}"...`);
|
|
2870
|
+
try {
|
|
2871
|
+
await (0, import_execa.default)(this.uvPath, args, {
|
|
2872
|
+
cwd,
|
|
2873
|
+
env: this.getVenvEnv(venvPath)
|
|
2874
|
+
});
|
|
2875
|
+
} catch (err) {
|
|
2876
|
+
const error = new Error(
|
|
2877
|
+
`Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
|
|
2878
|
+
);
|
|
2879
|
+
if (err && typeof err === "object") {
|
|
2880
|
+
if ("code" in err) {
|
|
2881
|
+
error.code = err.code;
|
|
2882
|
+
} else if ("signal" in err) {
|
|
2883
|
+
error.code = err.signal;
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
throw error;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
getVenvEnv(venvPath) {
|
|
2890
|
+
const binDir = isWin ? (0, import_path.join)(venvPath, "Scripts") : (0, import_path.join)(venvPath, "bin");
|
|
2891
|
+
const existingPath = process.env.PATH || "";
|
|
2892
|
+
return {
|
|
2893
|
+
...getProtectedUvEnv(process.env),
|
|
2894
|
+
VIRTUAL_ENV: venvPath,
|
|
2895
|
+
PATH: existingPath ? `${binDir}${import_path2.delimiter}${existingPath}` : binDir
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
async function getGlobalScriptsDir(pythonPath) {
|
|
2900
|
+
const code = `import sysconfig; print(sysconfig.get_path('scripts'))`;
|
|
2901
|
+
try {
|
|
2902
|
+
const { stdout } = await (0, import_execa.default)(pythonPath, ["-c", code]);
|
|
2903
|
+
const out = stdout.trim();
|
|
2904
|
+
return out || null;
|
|
2905
|
+
} catch (err) {
|
|
2906
|
+
(0, import_build_utils.debug)("Failed to resolve Python global scripts directory", err);
|
|
2907
|
+
return null;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
async function getUserScriptsDir(pythonPath) {
|
|
2911
|
+
const code = `import sys, sysconfig; print(sysconfig.get_path('scripts', scheme=('nt_user' if sys.platform == 'win32' else 'posix_user')))`.replace(
|
|
2912
|
+
/\n/g,
|
|
2913
|
+
" "
|
|
2914
|
+
);
|
|
2915
|
+
try {
|
|
2916
|
+
const { stdout } = await (0, import_execa.default)(pythonPath, ["-c", code]);
|
|
2917
|
+
const out = stdout.trim();
|
|
2918
|
+
return out || null;
|
|
2919
|
+
} catch (err) {
|
|
2920
|
+
(0, import_build_utils.debug)("Failed to resolve Python user scripts directory", err);
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
async function findUvBinary(pythonPath) {
|
|
2925
|
+
const found = import_which.default.sync("uv", { nothrow: true });
|
|
2926
|
+
if (found)
|
|
2927
|
+
return found;
|
|
2928
|
+
try {
|
|
2929
|
+
const globalScriptsDir = await getGlobalScriptsDir(pythonPath);
|
|
2930
|
+
if (globalScriptsDir) {
|
|
2931
|
+
const uvPath = (0, import_path.join)(globalScriptsDir, uvExec);
|
|
2932
|
+
if (import_fs.default.existsSync(uvPath))
|
|
2933
|
+
return uvPath;
|
|
2934
|
+
}
|
|
2935
|
+
} catch (err) {
|
|
2936
|
+
(0, import_build_utils.debug)("Failed to resolve Python global scripts directory", err);
|
|
2937
|
+
}
|
|
2938
|
+
try {
|
|
2939
|
+
const userScriptsDir = await getUserScriptsDir(pythonPath);
|
|
2940
|
+
if (userScriptsDir) {
|
|
2941
|
+
const uvPath = (0, import_path.join)(userScriptsDir, uvExec);
|
|
2942
|
+
if (import_fs.default.existsSync(uvPath))
|
|
2943
|
+
return uvPath;
|
|
2944
|
+
}
|
|
2945
|
+
} catch (err) {
|
|
2946
|
+
(0, import_build_utils.debug)("Failed to resolve Python user scripts directory", err);
|
|
2947
|
+
}
|
|
2948
|
+
try {
|
|
2949
|
+
const candidates = [];
|
|
2950
|
+
if (!isWin) {
|
|
2951
|
+
candidates.push((0, import_path.join)(import_os.default.homedir(), ".local", "bin", "uv"));
|
|
2952
|
+
candidates.push("/usr/local/bin/uv");
|
|
2953
|
+
candidates.push("/opt/homebrew/bin/uv");
|
|
2954
|
+
} else {
|
|
2955
|
+
candidates.push("C:\\Users\\Public\\uv\\uv.exe");
|
|
2956
|
+
}
|
|
2957
|
+
for (const p of candidates) {
|
|
2958
|
+
if (import_fs.default.existsSync(p))
|
|
2959
|
+
return p;
|
|
2960
|
+
}
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
(0, import_build_utils.debug)("Failed to resolve uv fallback paths", err);
|
|
2963
|
+
}
|
|
2964
|
+
return null;
|
|
2965
|
+
}
|
|
2966
|
+
async function getUvBinaryOrInstall(pythonPath) {
|
|
2967
|
+
const uvBin = await findUvBinary(pythonPath);
|
|
2968
|
+
if (uvBin)
|
|
2969
|
+
return uvBin;
|
|
2970
|
+
try {
|
|
2971
|
+
console.log("Installing uv...");
|
|
2972
|
+
await (0, import_execa.default)(
|
|
2973
|
+
pythonPath,
|
|
2974
|
+
[
|
|
2975
|
+
"-m",
|
|
2976
|
+
"pip",
|
|
2977
|
+
"install",
|
|
2978
|
+
"--disable-pip-version-check",
|
|
2979
|
+
"--no-cache-dir",
|
|
2980
|
+
"--user",
|
|
2981
|
+
`uv==${UV_VERSION}`
|
|
2982
|
+
],
|
|
2983
|
+
{ env: { ...process.env, PIP_USER: "1" } }
|
|
2984
|
+
);
|
|
2985
|
+
} catch (err) {
|
|
2986
|
+
throw new Error(
|
|
2987
|
+
`Failed to install uv via pip: ${err instanceof Error ? err.message : String(err)}`
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
const resolvedUvBin = await findUvBinary(pythonPath);
|
|
2991
|
+
if (!resolvedUvBin) {
|
|
2992
|
+
throw new Error("Unable to resolve uv binary after pip install");
|
|
2993
|
+
}
|
|
2994
|
+
console.log(`Installed uv at "${resolvedUvBin}"`);
|
|
2995
|
+
return resolvedUvBin;
|
|
2996
|
+
}
|
|
2997
|
+
function filterUnsafeUvPipArgs(args) {
|
|
2998
|
+
return args.filter((arg) => arg !== "--no-warn-script-location");
|
|
2999
|
+
}
|
|
3000
|
+
function getProtectedUvEnv(baseEnv = process.env) {
|
|
3001
|
+
return {
|
|
3002
|
+
...baseEnv,
|
|
3003
|
+
UV_PYTHON_DOWNLOADS: UV_PYTHON_DOWNLOADS_MODE
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
// src/utils.ts
|
|
3008
|
+
var isWin2 = process.platform === "win32";
|
|
2771
3009
|
var isInVirtualEnv = () => {
|
|
2772
3010
|
return process.env.VIRTUAL_ENV;
|
|
2773
3011
|
};
|
|
2774
3012
|
function getVenvBinDir(venvPath) {
|
|
2775
|
-
return (0,
|
|
3013
|
+
return (0, import_path3.join)(venvPath, isWin2 ? "Scripts" : "bin");
|
|
2776
3014
|
}
|
|
2777
3015
|
function useVirtualEnv(workPath, env, systemPython) {
|
|
2778
3016
|
const venvDirs = [".venv", "venv"];
|
|
2779
3017
|
let pythonCmd = systemPython;
|
|
2780
3018
|
for (const venv of venvDirs) {
|
|
2781
|
-
const venvRoot = (0,
|
|
2782
|
-
const binDir = process.platform === "win32" ? (0,
|
|
2783
|
-
const candidates = process.platform === "win32" ? [(0,
|
|
2784
|
-
const found = candidates.find((p) =>
|
|
3019
|
+
const venvRoot = (0, import_path3.join)(workPath, venv);
|
|
3020
|
+
const binDir = process.platform === "win32" ? (0, import_path3.join)(venvRoot, "Scripts") : (0, import_path3.join)(venvRoot, "bin");
|
|
3021
|
+
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")];
|
|
3022
|
+
const found = candidates.find((p) => import_fs2.default.existsSync(p));
|
|
2785
3023
|
if (found) {
|
|
2786
3024
|
pythonCmd = found;
|
|
2787
3025
|
env.VIRTUAL_ENV = venvRoot;
|
|
2788
|
-
env.PATH = `${binDir}${
|
|
3026
|
+
env.PATH = `${binDir}${import_path3.delimiter}${env.PATH || ""}`;
|
|
2789
3027
|
return { pythonCmd, venvRoot };
|
|
2790
3028
|
}
|
|
2791
3029
|
}
|
|
2792
3030
|
return { pythonCmd };
|
|
2793
3031
|
}
|
|
2794
|
-
function getProtectedUvEnv(baseEnv = process.env) {
|
|
2795
|
-
return {
|
|
2796
|
-
...baseEnv,
|
|
2797
|
-
UV_PYTHON_DOWNLOADS: UV_PYTHON_DOWNLOADS_MODE
|
|
2798
|
-
};
|
|
2799
|
-
}
|
|
2800
3032
|
function createVenvEnv(venvPath, baseEnv = process.env) {
|
|
2801
3033
|
const env = {
|
|
2802
3034
|
...getProtectedUvEnv(baseEnv),
|
|
@@ -2804,118 +3036,345 @@ function createVenvEnv(venvPath, baseEnv = process.env) {
|
|
|
2804
3036
|
};
|
|
2805
3037
|
const binDir = getVenvBinDir(venvPath);
|
|
2806
3038
|
const existingPath = env.PATH || process.env.PATH || "";
|
|
2807
|
-
env.PATH = existingPath ? `${binDir}${
|
|
3039
|
+
env.PATH = existingPath ? `${binDir}${import_path3.delimiter}${existingPath}` : binDir;
|
|
2808
3040
|
return env;
|
|
2809
3041
|
}
|
|
2810
3042
|
async function ensureVenv({
|
|
2811
3043
|
pythonPath,
|
|
2812
3044
|
venvPath
|
|
2813
3045
|
}) {
|
|
2814
|
-
const marker = (0,
|
|
3046
|
+
const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
|
|
2815
3047
|
try {
|
|
2816
|
-
await
|
|
3048
|
+
await import_fs2.default.promises.access(marker);
|
|
2817
3049
|
return;
|
|
2818
3050
|
} catch {
|
|
2819
3051
|
}
|
|
2820
|
-
await
|
|
2821
|
-
console.log(`Creating virtual environment at "${venvPath}"...`);
|
|
2822
|
-
await (0,
|
|
2823
|
-
}
|
|
2824
|
-
function getVenvPythonBin(venvPath) {
|
|
2825
|
-
return (0,
|
|
3052
|
+
await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
|
|
3053
|
+
console.log(`Creating virtual environment at "${venvPath}"...`);
|
|
3054
|
+
await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
|
|
3055
|
+
}
|
|
3056
|
+
function getVenvPythonBin(venvPath) {
|
|
3057
|
+
return (0, import_path3.join)(getVenvBinDir(venvPath), isWin2 ? "python.exe" : "python");
|
|
3058
|
+
}
|
|
3059
|
+
async function runPyprojectScript(workPath, scriptNames, env, useUserVirtualEnv = true) {
|
|
3060
|
+
const pyprojectPath = (0, import_path3.join)(workPath, "pyproject.toml");
|
|
3061
|
+
if (!import_fs2.default.existsSync(pyprojectPath))
|
|
3062
|
+
return false;
|
|
3063
|
+
let pyproject = null;
|
|
3064
|
+
try {
|
|
3065
|
+
pyproject = await (0, import_build_utils2.readConfigFile)(pyprojectPath);
|
|
3066
|
+
} catch {
|
|
3067
|
+
console.error("Failed to parse pyproject.toml");
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
const scripts = pyproject?.tool?.vercel?.scripts || {};
|
|
3071
|
+
const candidates = typeof scriptNames === "string" ? [scriptNames] : Array.from(scriptNames);
|
|
3072
|
+
const scriptToRun = candidates.find((name) => Boolean(scripts[name]));
|
|
3073
|
+
if (!scriptToRun)
|
|
3074
|
+
return false;
|
|
3075
|
+
const systemPython = process.platform === "win32" ? "python" : "python3";
|
|
3076
|
+
const finalEnv = { ...process.env, ...env };
|
|
3077
|
+
if (useUserVirtualEnv) {
|
|
3078
|
+
useVirtualEnv(workPath, finalEnv, systemPython);
|
|
3079
|
+
}
|
|
3080
|
+
const scriptCommand = scripts[scriptToRun];
|
|
3081
|
+
if (typeof scriptCommand === "string" && scriptCommand.trim()) {
|
|
3082
|
+
console.log(`Executing: ${scriptCommand}`);
|
|
3083
|
+
await (0, import_build_utils2.execCommand)(scriptCommand, {
|
|
3084
|
+
cwd: workPath,
|
|
3085
|
+
env: finalEnv
|
|
3086
|
+
});
|
|
3087
|
+
return true;
|
|
3088
|
+
}
|
|
3089
|
+
return false;
|
|
3090
|
+
}
|
|
3091
|
+
function findDir({
|
|
3092
|
+
file,
|
|
3093
|
+
entryDirectory,
|
|
3094
|
+
workPath,
|
|
3095
|
+
fsFiles
|
|
3096
|
+
}) {
|
|
3097
|
+
if (fsFiles[(0, import_path3.join)(entryDirectory, file)]) {
|
|
3098
|
+
return (0, import_path3.join)(workPath, entryDirectory);
|
|
3099
|
+
}
|
|
3100
|
+
if (fsFiles[file]) {
|
|
3101
|
+
return workPath;
|
|
3102
|
+
}
|
|
3103
|
+
return null;
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
// src/version.ts
|
|
3107
|
+
var import_build_utils3 = require("@vercel/build-utils");
|
|
3108
|
+
var DEFAULT_PYTHON_VERSION = "3.12";
|
|
3109
|
+
var allOptions = [
|
|
3110
|
+
{
|
|
3111
|
+
version: "3.14",
|
|
3112
|
+
pipPath: "pip3.14",
|
|
3113
|
+
pythonPath: "python3.14",
|
|
3114
|
+
runtime: "python3.14"
|
|
3115
|
+
},
|
|
3116
|
+
{
|
|
3117
|
+
version: "3.13",
|
|
3118
|
+
pipPath: "pip3.13",
|
|
3119
|
+
pythonPath: "python3.13",
|
|
3120
|
+
runtime: "python3.13"
|
|
3121
|
+
},
|
|
3122
|
+
{
|
|
3123
|
+
version: "3.12",
|
|
3124
|
+
pipPath: "pip3.12",
|
|
3125
|
+
pythonPath: "python3.12",
|
|
3126
|
+
runtime: "python3.12"
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
version: "3.11",
|
|
3130
|
+
pipPath: "pip3.11",
|
|
3131
|
+
pythonPath: "python3.11",
|
|
3132
|
+
runtime: "python3.11"
|
|
3133
|
+
},
|
|
3134
|
+
{
|
|
3135
|
+
version: "3.10",
|
|
3136
|
+
pipPath: "pip3.10",
|
|
3137
|
+
pythonPath: "python3.10",
|
|
3138
|
+
runtime: "python3.10"
|
|
3139
|
+
},
|
|
3140
|
+
{
|
|
3141
|
+
version: "3.9",
|
|
3142
|
+
pipPath: "pip3.9",
|
|
3143
|
+
pythonPath: "python3.9",
|
|
3144
|
+
runtime: "python3.9"
|
|
3145
|
+
},
|
|
3146
|
+
{
|
|
3147
|
+
version: "3.6",
|
|
3148
|
+
pipPath: "pip3.6",
|
|
3149
|
+
pythonPath: "python3.6",
|
|
3150
|
+
runtime: "python3.6",
|
|
3151
|
+
discontinueDate: /* @__PURE__ */ new Date("2022-07-18")
|
|
3152
|
+
}
|
|
3153
|
+
];
|
|
3154
|
+
function getDevPythonVersion() {
|
|
3155
|
+
return {
|
|
3156
|
+
version: "3",
|
|
3157
|
+
pipPath: "pip3",
|
|
3158
|
+
pythonPath: "python3",
|
|
3159
|
+
runtime: "python3"
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
function getDefaultPythonVersion({
|
|
3163
|
+
isDev
|
|
3164
|
+
}) {
|
|
3165
|
+
if (isDev) {
|
|
3166
|
+
return getDevPythonVersion();
|
|
3167
|
+
}
|
|
3168
|
+
const defaultOption = allOptions.find(
|
|
3169
|
+
(opt) => opt.version === DEFAULT_PYTHON_VERSION && isInstalled(opt)
|
|
3170
|
+
);
|
|
3171
|
+
if (defaultOption) {
|
|
3172
|
+
return defaultOption;
|
|
3173
|
+
}
|
|
3174
|
+
const selection = allOptions.find(isInstalled);
|
|
3175
|
+
if (!selection) {
|
|
3176
|
+
throw new import_build_utils3.NowBuildError({
|
|
3177
|
+
code: "PYTHON_NOT_FOUND",
|
|
3178
|
+
link: "https://vercel.link/python-version",
|
|
3179
|
+
message: `Unable to find any supported Python versions.`
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
return selection;
|
|
3183
|
+
}
|
|
3184
|
+
function parseVersionTuple(input) {
|
|
3185
|
+
const cleaned = input.trim().replace(/\s+/g, "");
|
|
3186
|
+
const m = cleaned.match(/^(\d+)(?:\.(\d+))?/);
|
|
3187
|
+
if (!m)
|
|
3188
|
+
return null;
|
|
3189
|
+
const major = Number(m[1]);
|
|
3190
|
+
const minor = m[2] !== void 0 ? Number(m[2]) : 0;
|
|
3191
|
+
if (Number.isNaN(major) || Number.isNaN(minor))
|
|
3192
|
+
return null;
|
|
3193
|
+
return [major, minor];
|
|
3194
|
+
}
|
|
3195
|
+
function compareTuples(a, b) {
|
|
3196
|
+
if (a[0] !== b[0])
|
|
3197
|
+
return a[0] - b[0];
|
|
3198
|
+
return a[1] - b[1];
|
|
3199
|
+
}
|
|
3200
|
+
function parseSpecifier(spec) {
|
|
3201
|
+
const s = spec.trim();
|
|
3202
|
+
const m = s.match(
|
|
3203
|
+
/^(<=|>=|==|!=|~=|<|>)\s*([0-9]+(?:\.[0-9]+)?)(?:\.[0-9]+)?(?:\.\*)?$/
|
|
3204
|
+
) || // Bare version like "3.11" or "3.11.4" -> implied ==
|
|
3205
|
+
s.match(/^()([0-9]+(?:\.[0-9]+)?)(?:\.[0-9]+)?(?:\.\*)?$/);
|
|
3206
|
+
if (!m)
|
|
3207
|
+
return null;
|
|
3208
|
+
const op = m[1] || "==";
|
|
3209
|
+
const vt = parseVersionTuple(m[2]);
|
|
3210
|
+
if (!vt)
|
|
3211
|
+
return null;
|
|
3212
|
+
return { op, ver: vt };
|
|
3213
|
+
}
|
|
3214
|
+
function satisfies(candidate, spec) {
|
|
3215
|
+
const cmp = compareTuples(candidate, spec.ver);
|
|
3216
|
+
switch (spec.op) {
|
|
3217
|
+
case "==":
|
|
3218
|
+
return cmp === 0;
|
|
3219
|
+
case "!=":
|
|
3220
|
+
return cmp !== 0;
|
|
3221
|
+
case "<":
|
|
3222
|
+
return cmp < 0;
|
|
3223
|
+
case "<=":
|
|
3224
|
+
return cmp <= 0;
|
|
3225
|
+
case ">":
|
|
3226
|
+
return cmp > 0;
|
|
3227
|
+
case ">=":
|
|
3228
|
+
return cmp >= 0;
|
|
3229
|
+
case "~=": {
|
|
3230
|
+
const lowerOk = cmp >= 0;
|
|
3231
|
+
const upper = [spec.ver[0], spec.ver[1] + 1];
|
|
3232
|
+
return lowerOk && compareTuples(candidate, upper) < 0;
|
|
3233
|
+
}
|
|
3234
|
+
default:
|
|
3235
|
+
return false;
|
|
3236
|
+
}
|
|
2826
3237
|
}
|
|
2827
|
-
|
|
2828
|
-
const
|
|
2829
|
-
if (!
|
|
2830
|
-
return
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
3238
|
+
function selectFromRequiresPython(expr) {
|
|
3239
|
+
const raw = expr.trim();
|
|
3240
|
+
if (!raw)
|
|
3241
|
+
return void 0;
|
|
3242
|
+
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3243
|
+
const specifiers = [];
|
|
3244
|
+
for (const p of parts) {
|
|
3245
|
+
const sp = parseSpecifier(p);
|
|
3246
|
+
if (sp)
|
|
3247
|
+
specifiers.push(sp);
|
|
2837
3248
|
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
const scriptToRun = candidates.find((name) => Boolean(scripts[name]));
|
|
2841
|
-
if (!scriptToRun)
|
|
2842
|
-
return false;
|
|
2843
|
-
const systemPython = process.platform === "win32" ? "python" : "python3";
|
|
2844
|
-
const finalEnv = { ...process.env, ...env };
|
|
2845
|
-
if (useUserVirtualEnv) {
|
|
2846
|
-
useVirtualEnv(workPath, finalEnv, systemPython);
|
|
3249
|
+
if (specifiers.length === 0) {
|
|
3250
|
+
return allOptions.find((o) => o.version === raw);
|
|
2847
3251
|
}
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
3252
|
+
const matches = allOptions.filter((opt) => {
|
|
3253
|
+
const vt = parseVersionTuple(opt.version);
|
|
3254
|
+
return specifiers.every((sp) => satisfies(vt, sp));
|
|
3255
|
+
});
|
|
3256
|
+
if (matches.length === 0)
|
|
3257
|
+
return void 0;
|
|
3258
|
+
const defaultMatch = matches.find(
|
|
3259
|
+
(opt) => opt.version === DEFAULT_PYTHON_VERSION && isInstalled(opt)
|
|
3260
|
+
);
|
|
3261
|
+
if (defaultMatch) {
|
|
3262
|
+
return defaultMatch;
|
|
2856
3263
|
}
|
|
2857
|
-
|
|
3264
|
+
const installedMatch = matches.find(isInstalled);
|
|
3265
|
+
return installedMatch ?? matches[0];
|
|
2858
3266
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
if (
|
|
2864
|
-
|
|
3267
|
+
function getSupportedPythonVersion({
|
|
3268
|
+
isDev,
|
|
3269
|
+
declaredPythonVersion
|
|
3270
|
+
}) {
|
|
3271
|
+
if (isDev) {
|
|
3272
|
+
return getDevPythonVersion();
|
|
2865
3273
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
)
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
3274
|
+
let selection = getDefaultPythonVersion({ isDev: false });
|
|
3275
|
+
if (declaredPythonVersion) {
|
|
3276
|
+
const { version: version2, source } = declaredPythonVersion;
|
|
3277
|
+
let requested;
|
|
3278
|
+
if (source === "pyproject.toml" || source === ".python-version") {
|
|
3279
|
+
requested = selectFromRequiresPython(version2);
|
|
3280
|
+
} else {
|
|
3281
|
+
requested = allOptions.find((o) => o.version === version2);
|
|
3282
|
+
}
|
|
3283
|
+
if (requested) {
|
|
3284
|
+
if (isDiscontinued(requested)) {
|
|
3285
|
+
throw new import_build_utils3.NowBuildError({
|
|
3286
|
+
code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
|
|
3287
|
+
link: "https://vercel.link/python-version",
|
|
3288
|
+
message: `Python version "${requested.version}" detected in ${source} is discontinued and must be upgraded.`
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
if (isInstalled(requested)) {
|
|
3292
|
+
selection = requested;
|
|
3293
|
+
console.log(`Using Python ${selection.version} from ${source}`);
|
|
3294
|
+
} else {
|
|
3295
|
+
console.warn(
|
|
3296
|
+
`Warning: Python version "${version2}" detected in ${source} is not installed and will be ignored. https://vercel.link/python-version`
|
|
3297
|
+
);
|
|
3298
|
+
console.log(`Using python version: ${selection.version}`);
|
|
2881
3299
|
}
|
|
3300
|
+
} else {
|
|
3301
|
+
console.warn(
|
|
3302
|
+
`Warning: Python version "${version2}" detected in ${source} is invalid and will be ignored. https://vercel.link/python-version`
|
|
3303
|
+
);
|
|
3304
|
+
console.log(`Using python version: ${selection.version}`);
|
|
2882
3305
|
}
|
|
2883
|
-
|
|
3306
|
+
} else {
|
|
3307
|
+
console.log(
|
|
3308
|
+
`No Python version specified in .python-version, pyproject.toml, or Pipfile.lock. Using python version: ${selection.version}`
|
|
3309
|
+
);
|
|
2884
3310
|
}
|
|
3311
|
+
if (isDiscontinued(selection)) {
|
|
3312
|
+
throw new import_build_utils3.NowBuildError({
|
|
3313
|
+
code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
|
|
3314
|
+
link: "https://vercel.link/python-version",
|
|
3315
|
+
message: `Python version "${selection.version}" declared in project configuration is discontinued and must be upgraded.`
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
if (selection.discontinueDate) {
|
|
3319
|
+
const d = selection.discontinueDate.toISOString().split("T")[0];
|
|
3320
|
+
const srcSuffix = declaredPythonVersion ? `detected in ${declaredPythonVersion.source}` : "selected by runtime";
|
|
3321
|
+
console.warn(
|
|
3322
|
+
`Error: Python version "${selection.version}" ${srcSuffix} has reached End-of-Life. Deployments created on or after ${d} will fail to build. https://vercel.link/python-version`
|
|
3323
|
+
);
|
|
3324
|
+
}
|
|
3325
|
+
return selection;
|
|
2885
3326
|
}
|
|
2886
|
-
function
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
if (
|
|
2893
|
-
return
|
|
3327
|
+
function isDiscontinued({ discontinueDate }) {
|
|
3328
|
+
const today = Date.now();
|
|
3329
|
+
return discontinueDate !== void 0 && discontinueDate.getTime() <= today;
|
|
3330
|
+
}
|
|
3331
|
+
var installedPythonsCache = null;
|
|
3332
|
+
function getInstalledPythons() {
|
|
3333
|
+
if (installedPythonsCache !== null) {
|
|
3334
|
+
return installedPythonsCache;
|
|
2894
3335
|
}
|
|
2895
|
-
|
|
2896
|
-
|
|
3336
|
+
const uvPath = findUvInPath();
|
|
3337
|
+
if (!uvPath) {
|
|
3338
|
+
throw new import_build_utils3.NowBuildError({
|
|
3339
|
+
code: "UV_ERROR",
|
|
3340
|
+
link: "https://vercel.link/python-version",
|
|
3341
|
+
message: "uv is required but was not found in PATH."
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
const uv = new UvRunner(uvPath);
|
|
3345
|
+
installedPythonsCache = uv.listInstalledPythons();
|
|
3346
|
+
return installedPythonsCache;
|
|
3347
|
+
}
|
|
3348
|
+
function isInstalled({ version: version2 }) {
|
|
3349
|
+
try {
|
|
3350
|
+
const installed = getInstalledPythons();
|
|
3351
|
+
return installed.has(version2);
|
|
3352
|
+
} catch (err) {
|
|
3353
|
+
throw new import_build_utils3.NowBuildError({
|
|
3354
|
+
code: "UV_ERROR",
|
|
3355
|
+
link: "https://vercel.link/python-version",
|
|
3356
|
+
message: err instanceof Error ? err.message : String(err)
|
|
3357
|
+
});
|
|
2897
3358
|
}
|
|
2898
|
-
return null;
|
|
2899
3359
|
}
|
|
2900
3360
|
|
|
2901
3361
|
// src/install.ts
|
|
2902
|
-
var
|
|
2903
|
-
var uvExec = isWin2 ? "uv.exe" : "uv";
|
|
3362
|
+
var isWin3 = process.platform === "win32";
|
|
2904
3363
|
var makeDependencyCheckCode = (dependency) => `
|
|
2905
3364
|
from importlib import util
|
|
2906
3365
|
dep = '${dependency}'.replace('-', '_')
|
|
2907
3366
|
spec = util.find_spec(dep)
|
|
2908
3367
|
print(spec.origin)
|
|
2909
3368
|
`;
|
|
2910
|
-
async function
|
|
3369
|
+
async function isInstalled2(pythonPath, dependency, cwd) {
|
|
2911
3370
|
try {
|
|
2912
|
-
const { stdout } = await (0,
|
|
3371
|
+
const { stdout } = await (0, import_execa3.default)(
|
|
2913
3372
|
pythonPath,
|
|
2914
3373
|
["-c", makeDependencyCheckCode(dependency)],
|
|
2915
3374
|
{
|
|
2916
3375
|
stdio: "pipe",
|
|
2917
3376
|
cwd,
|
|
2918
|
-
env: { ...process.env, PYTHONPATH: (0,
|
|
3377
|
+
env: { ...process.env, PYTHONPATH: (0, import_path4.join)(cwd, resolveVendorDir()) }
|
|
2919
3378
|
}
|
|
2920
3379
|
);
|
|
2921
3380
|
return stdout.startsWith(cwd);
|
|
@@ -2932,13 +3391,13 @@ pkg_resources.require(dependencies)
|
|
|
2932
3391
|
`;
|
|
2933
3392
|
async function areRequirementsInstalled(pythonPath, requirementsPath, cwd) {
|
|
2934
3393
|
try {
|
|
2935
|
-
await (0,
|
|
3394
|
+
await (0, import_execa3.default)(
|
|
2936
3395
|
pythonPath,
|
|
2937
3396
|
["-c", makeRequirementsCheckCode(requirementsPath)],
|
|
2938
3397
|
{
|
|
2939
3398
|
stdio: "pipe",
|
|
2940
3399
|
cwd,
|
|
2941
|
-
env: { ...process.env, PYTHONPATH: (0,
|
|
3400
|
+
env: { ...process.env, PYTHONPATH: (0, import_path4.join)(cwd, resolveVendorDir()) }
|
|
2942
3401
|
}
|
|
2943
3402
|
);
|
|
2944
3403
|
return true;
|
|
@@ -2946,24 +3405,6 @@ async function areRequirementsInstalled(pythonPath, requirementsPath, cwd) {
|
|
|
2946
3405
|
return false;
|
|
2947
3406
|
}
|
|
2948
3407
|
}
|
|
2949
|
-
async function runUvSync({
|
|
2950
|
-
uvPath,
|
|
2951
|
-
venvPath,
|
|
2952
|
-
projectDir,
|
|
2953
|
-
locked
|
|
2954
|
-
}) {
|
|
2955
|
-
const args = ["sync", "--active", "--no-dev", "--link-mode", "copy"];
|
|
2956
|
-
if (locked) {
|
|
2957
|
-
args.push("--locked");
|
|
2958
|
-
}
|
|
2959
|
-
args.push("--no-editable");
|
|
2960
|
-
await runUvCommand({
|
|
2961
|
-
uvPath,
|
|
2962
|
-
args,
|
|
2963
|
-
cwd: projectDir,
|
|
2964
|
-
venvPath
|
|
2965
|
-
});
|
|
2966
|
-
}
|
|
2967
3408
|
async function getSitePackagesDirs(pythonBin) {
|
|
2968
3409
|
const code = `
|
|
2969
3410
|
import json
|
|
@@ -2975,14 +3416,14 @@ for key in ("purelib", "platlib"):
|
|
|
2975
3416
|
paths.append(candidate)
|
|
2976
3417
|
print(json.dumps(paths))
|
|
2977
3418
|
`.trim();
|
|
2978
|
-
const { stdout } = await (0,
|
|
3419
|
+
const { stdout } = await (0, import_execa3.default)(pythonBin, ["-c", code]);
|
|
2979
3420
|
try {
|
|
2980
3421
|
const parsed = JSON.parse(stdout);
|
|
2981
3422
|
if (Array.isArray(parsed)) {
|
|
2982
3423
|
return parsed.filter((p) => typeof p === "string");
|
|
2983
3424
|
}
|
|
2984
3425
|
} catch (err) {
|
|
2985
|
-
(0,
|
|
3426
|
+
(0, import_build_utils4.debug)("Failed to parse site-packages output", err);
|
|
2986
3427
|
}
|
|
2987
3428
|
return [];
|
|
2988
3429
|
}
|
|
@@ -3033,26 +3474,26 @@ async function detectInstallSource({
|
|
|
3033
3474
|
let manifestType = null;
|
|
3034
3475
|
if (uvLockDir && pyprojectDir) {
|
|
3035
3476
|
manifestType = "uv.lock";
|
|
3036
|
-
manifestPath = (0,
|
|
3477
|
+
manifestPath = (0, import_path4.join)(uvLockDir, "uv.lock");
|
|
3037
3478
|
} else if (pyprojectDir) {
|
|
3038
3479
|
manifestType = "pyproject.toml";
|
|
3039
|
-
manifestPath = (0,
|
|
3480
|
+
manifestPath = (0, import_path4.join)(pyprojectDir, "pyproject.toml");
|
|
3040
3481
|
} else if (pipfileLockDir) {
|
|
3041
3482
|
manifestType = "Pipfile.lock";
|
|
3042
|
-
manifestPath = (0,
|
|
3483
|
+
manifestPath = (0, import_path4.join)(pipfileLockDir, "Pipfile.lock");
|
|
3043
3484
|
} else if (pipfileDir) {
|
|
3044
3485
|
manifestType = "Pipfile";
|
|
3045
|
-
manifestPath = (0,
|
|
3486
|
+
manifestPath = (0, import_path4.join)(pipfileDir, "Pipfile");
|
|
3046
3487
|
} else if (requirementsDir) {
|
|
3047
3488
|
manifestType = "requirements.txt";
|
|
3048
|
-
manifestPath = (0,
|
|
3489
|
+
manifestPath = (0, import_path4.join)(requirementsDir, "requirements.txt");
|
|
3049
3490
|
}
|
|
3050
3491
|
let manifestContent;
|
|
3051
3492
|
if (manifestPath) {
|
|
3052
3493
|
try {
|
|
3053
|
-
manifestContent = await
|
|
3494
|
+
manifestContent = await import_fs3.default.promises.readFile(manifestPath, "utf8");
|
|
3054
3495
|
} catch (err) {
|
|
3055
|
-
(0,
|
|
3496
|
+
(0, import_build_utils4.debug)("Failed to read install manifest contents", err);
|
|
3056
3497
|
}
|
|
3057
3498
|
}
|
|
3058
3499
|
return { manifestPath, manifestType, manifestContent };
|
|
@@ -3062,7 +3503,7 @@ async function createPyprojectToml({
|
|
|
3062
3503
|
pyprojectPath,
|
|
3063
3504
|
dependencies
|
|
3064
3505
|
}) {
|
|
3065
|
-
const requiresPython =
|
|
3506
|
+
const requiresPython = `~=${DEFAULT_PYTHON_VERSION}.0`;
|
|
3066
3507
|
const depsToml = dependencies.length > 0 ? [
|
|
3067
3508
|
"dependencies = [",
|
|
3068
3509
|
...dependencies.map((dep) => ` "${dep}",`),
|
|
@@ -3079,47 +3520,7 @@ async function createPyprojectToml({
|
|
|
3079
3520
|
depsToml,
|
|
3080
3521
|
""
|
|
3081
3522
|
].join("\n");
|
|
3082
|
-
await
|
|
3083
|
-
}
|
|
3084
|
-
async function uvLock({
|
|
3085
|
-
projectDir,
|
|
3086
|
-
uvPath
|
|
3087
|
-
}) {
|
|
3088
|
-
const args = ["lock"];
|
|
3089
|
-
const pretty = `${uvPath} ${args.join(" ")}`;
|
|
3090
|
-
(0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
|
|
3091
|
-
try {
|
|
3092
|
-
await (0, import_execa2.default)(uvPath, args, { cwd: projectDir, env: getProtectedUvEnv() });
|
|
3093
|
-
} catch (err) {
|
|
3094
|
-
throw new Error(
|
|
3095
|
-
`Failed to run "${pretty}": ${err instanceof Error ? err.message : String(err)}`
|
|
3096
|
-
);
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
async function uvAddDependencies({
|
|
3100
|
-
projectDir,
|
|
3101
|
-
uvPath,
|
|
3102
|
-
venvPath,
|
|
3103
|
-
dependencies
|
|
3104
|
-
}) {
|
|
3105
|
-
const toAdd = dependencies.filter(Boolean);
|
|
3106
|
-
if (!toAdd.length)
|
|
3107
|
-
return;
|
|
3108
|
-
const args = ["add", "--active", ...toAdd];
|
|
3109
|
-
const pretty = `${uvPath} ${args.join(" ")}`;
|
|
3110
|
-
(0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
|
|
3111
|
-
await runUvCommand({ uvPath, args, cwd: projectDir, venvPath });
|
|
3112
|
-
}
|
|
3113
|
-
async function uvAddFromFile({
|
|
3114
|
-
projectDir,
|
|
3115
|
-
uvPath,
|
|
3116
|
-
venvPath,
|
|
3117
|
-
requirementsPath
|
|
3118
|
-
}) {
|
|
3119
|
-
const args = ["add", "--active", "-r", requirementsPath];
|
|
3120
|
-
const pretty = `${uvPath} ${args.join(" ")}`;
|
|
3121
|
-
(0, import_build_utils2.debug)(`Running "${pretty}" in ${projectDir}...`);
|
|
3122
|
-
await runUvCommand({ uvPath, args, cwd: projectDir, venvPath });
|
|
3523
|
+
await import_fs3.default.promises.writeFile(pyprojectPath, content);
|
|
3123
3524
|
}
|
|
3124
3525
|
function getDependencyName(spec) {
|
|
3125
3526
|
const match = spec.match(/^[A-Za-z0-9_.-]+/);
|
|
@@ -3131,10 +3532,10 @@ async function filterMissingRuntimeDependencies({
|
|
|
3131
3532
|
}) {
|
|
3132
3533
|
let declared = [];
|
|
3133
3534
|
try {
|
|
3134
|
-
const config = await (0,
|
|
3535
|
+
const config = await (0, import_build_utils4.readConfigFile)(pyprojectPath);
|
|
3135
3536
|
declared = config?.project?.dependencies || [];
|
|
3136
3537
|
} catch (err) {
|
|
3137
|
-
(0,
|
|
3538
|
+
(0, import_build_utils4.debug)("Failed to parse pyproject.toml when filtering runtime deps", err);
|
|
3138
3539
|
}
|
|
3139
3540
|
const declaredNames = new Set(declared.map(getDependencyName));
|
|
3140
3541
|
return runtimeDependencies.filter((spec) => {
|
|
@@ -3143,12 +3544,12 @@ async function filterMissingRuntimeDependencies({
|
|
|
3143
3544
|
});
|
|
3144
3545
|
}
|
|
3145
3546
|
function findUvLockUpwards(startDir, repoRootPath) {
|
|
3146
|
-
const start = (0,
|
|
3147
|
-
const base = repoRootPath ? (0,
|
|
3148
|
-
for (const dir of (0,
|
|
3149
|
-
const lockPath = (0,
|
|
3150
|
-
const pyprojectPath = (0,
|
|
3151
|
-
if (
|
|
3547
|
+
const start = (0, import_path4.resolve)(startDir);
|
|
3548
|
+
const base = repoRootPath ? (0, import_path4.resolve)(repoRootPath) : void 0;
|
|
3549
|
+
for (const dir of (0, import_build_utils4.traverseUpDirectories)({ start, base })) {
|
|
3550
|
+
const lockPath = (0, import_path4.join)(dir, "uv.lock");
|
|
3551
|
+
const pyprojectPath = (0, import_path4.join)(dir, "pyproject.toml");
|
|
3552
|
+
if (import_fs3.default.existsSync(lockPath) && import_fs3.default.existsSync(pyprojectPath)) {
|
|
3152
3553
|
return lockPath;
|
|
3153
3554
|
}
|
|
3154
3555
|
}
|
|
@@ -3161,11 +3562,12 @@ async function ensureUvProject({
|
|
|
3161
3562
|
repoRootPath,
|
|
3162
3563
|
pythonPath,
|
|
3163
3564
|
pipPath,
|
|
3164
|
-
|
|
3565
|
+
uv,
|
|
3165
3566
|
venvPath,
|
|
3166
3567
|
meta,
|
|
3167
3568
|
runtimeDependencies
|
|
3168
3569
|
}) {
|
|
3570
|
+
const uvPath = uv.getPath();
|
|
3169
3571
|
const installInfo = await detectInstallSource({
|
|
3170
3572
|
workPath,
|
|
3171
3573
|
entryDirectory,
|
|
@@ -3179,9 +3581,9 @@ async function ensureUvProject({
|
|
|
3179
3581
|
if (!manifestPath) {
|
|
3180
3582
|
throw new Error("Expected uv.lock path to be resolved, but it was null");
|
|
3181
3583
|
}
|
|
3182
|
-
projectDir = (0,
|
|
3183
|
-
pyprojectPath = (0,
|
|
3184
|
-
if (!
|
|
3584
|
+
projectDir = (0, import_path4.dirname)(manifestPath);
|
|
3585
|
+
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3586
|
+
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3185
3587
|
throw new Error(
|
|
3186
3588
|
`Expected "pyproject.toml" next to "uv.lock" in "${projectDir}"`
|
|
3187
3589
|
);
|
|
@@ -3194,14 +3596,14 @@ async function ensureUvProject({
|
|
|
3194
3596
|
"Expected pyproject.toml path to be resolved, but it was null"
|
|
3195
3597
|
);
|
|
3196
3598
|
}
|
|
3197
|
-
projectDir = (0,
|
|
3599
|
+
projectDir = (0, import_path4.dirname)(manifestPath);
|
|
3198
3600
|
pyprojectPath = manifestPath;
|
|
3199
3601
|
console.log("Installing required dependencies from pyproject.toml...");
|
|
3200
3602
|
const workspaceLock = findUvLockUpwards(projectDir, repoRootPath);
|
|
3201
3603
|
if (workspaceLock) {
|
|
3202
3604
|
lockPath = workspaceLock;
|
|
3203
3605
|
} else {
|
|
3204
|
-
await
|
|
3606
|
+
await uv.lock(projectDir);
|
|
3205
3607
|
}
|
|
3206
3608
|
} else if (manifestType === "Pipfile.lock" || manifestType === "Pipfile") {
|
|
3207
3609
|
if (!manifestPath) {
|
|
@@ -3209,7 +3611,7 @@ async function ensureUvProject({
|
|
|
3209
3611
|
"Expected Pipfile/Pipfile.lock path to be resolved, but it was null"
|
|
3210
3612
|
);
|
|
3211
3613
|
}
|
|
3212
|
-
projectDir = (0,
|
|
3614
|
+
projectDir = (0, import_path4.dirname)(manifestPath);
|
|
3213
3615
|
console.log(`Installing required dependencies from ${manifestType}...`);
|
|
3214
3616
|
const exportedReq = await exportRequirementsFromPipfile({
|
|
3215
3617
|
pythonPath,
|
|
@@ -3218,18 +3620,17 @@ async function ensureUvProject({
|
|
|
3218
3620
|
projectDir,
|
|
3219
3621
|
meta
|
|
3220
3622
|
});
|
|
3221
|
-
pyprojectPath = (0,
|
|
3222
|
-
if (!
|
|
3623
|
+
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3624
|
+
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3223
3625
|
await createPyprojectToml({
|
|
3224
3626
|
projectName: "app",
|
|
3225
3627
|
pyprojectPath,
|
|
3226
3628
|
dependencies: []
|
|
3227
3629
|
});
|
|
3228
3630
|
}
|
|
3229
|
-
await
|
|
3230
|
-
projectDir,
|
|
3231
|
-
uvPath,
|
|
3631
|
+
await uv.addFromFile({
|
|
3232
3632
|
venvPath,
|
|
3633
|
+
projectDir,
|
|
3233
3634
|
requirementsPath: exportedReq
|
|
3234
3635
|
});
|
|
3235
3636
|
} else if (manifestType === "requirements.txt") {
|
|
@@ -3238,27 +3639,26 @@ async function ensureUvProject({
|
|
|
3238
3639
|
"Expected requirements.txt path to be resolved, but it was null"
|
|
3239
3640
|
);
|
|
3240
3641
|
}
|
|
3241
|
-
projectDir = (0,
|
|
3242
|
-
pyprojectPath = (0,
|
|
3642
|
+
projectDir = (0, import_path4.dirname)(manifestPath);
|
|
3643
|
+
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3243
3644
|
console.log(
|
|
3244
3645
|
"Installing required dependencies from requirements.txt with uv..."
|
|
3245
3646
|
);
|
|
3246
|
-
if (!
|
|
3647
|
+
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3247
3648
|
await createPyprojectToml({
|
|
3248
3649
|
projectName: "app",
|
|
3249
3650
|
pyprojectPath,
|
|
3250
3651
|
dependencies: []
|
|
3251
3652
|
});
|
|
3252
3653
|
}
|
|
3253
|
-
await
|
|
3254
|
-
projectDir,
|
|
3255
|
-
uvPath,
|
|
3654
|
+
await uv.addFromFile({
|
|
3256
3655
|
venvPath,
|
|
3656
|
+
projectDir,
|
|
3257
3657
|
requirementsPath: manifestPath
|
|
3258
3658
|
});
|
|
3259
3659
|
} else {
|
|
3260
3660
|
projectDir = workPath;
|
|
3261
|
-
pyprojectPath = (0,
|
|
3661
|
+
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3262
3662
|
console.log(
|
|
3263
3663
|
"No Python manifest found; creating an empty pyproject.toml and uv.lock..."
|
|
3264
3664
|
);
|
|
@@ -3267,7 +3667,7 @@ async function ensureUvProject({
|
|
|
3267
3667
|
pyprojectPath,
|
|
3268
3668
|
dependencies: []
|
|
3269
3669
|
});
|
|
3270
|
-
await
|
|
3670
|
+
await uv.lock(projectDir);
|
|
3271
3671
|
}
|
|
3272
3672
|
if (runtimeDependencies.length) {
|
|
3273
3673
|
const missingRuntimeDeps = await filterMissingRuntimeDependencies({
|
|
@@ -3275,44 +3675,18 @@ async function ensureUvProject({
|
|
|
3275
3675
|
runtimeDependencies
|
|
3276
3676
|
});
|
|
3277
3677
|
if (missingRuntimeDeps.length) {
|
|
3278
|
-
await
|
|
3279
|
-
projectDir,
|
|
3280
|
-
uvPath,
|
|
3678
|
+
await uv.addDependencies({
|
|
3281
3679
|
venvPath,
|
|
3680
|
+
projectDir,
|
|
3282
3681
|
dependencies: missingRuntimeDeps
|
|
3283
3682
|
});
|
|
3284
3683
|
}
|
|
3285
3684
|
}
|
|
3286
|
-
const resolvedLockPath = lockPath &&
|
|
3685
|
+
const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : findUvLockUpwards(projectDir, repoRootPath) || (0, import_path4.join)(projectDir, "uv.lock");
|
|
3287
3686
|
return { projectDir, pyprojectPath, lockPath: resolvedLockPath };
|
|
3288
3687
|
}
|
|
3289
|
-
async function getGlobalScriptsDir(pythonPath) {
|
|
3290
|
-
const code = `import sysconfig; print(sysconfig.get_path('scripts'))`;
|
|
3291
|
-
try {
|
|
3292
|
-
const { stdout } = await (0, import_execa2.default)(pythonPath, ["-c", code]);
|
|
3293
|
-
const out = stdout.trim();
|
|
3294
|
-
return out || null;
|
|
3295
|
-
} catch (err) {
|
|
3296
|
-
(0, import_build_utils2.debug)("Failed to resolve Python global scripts directory", err);
|
|
3297
|
-
return null;
|
|
3298
|
-
}
|
|
3299
|
-
}
|
|
3300
|
-
async function getUserScriptsDir(pythonPath) {
|
|
3301
|
-
const code = `import sys, sysconfig; print(sysconfig.get_path('scripts', scheme=('nt_user' if sys.platform == 'win32' else 'posix_user')))`.replace(
|
|
3302
|
-
/\n/g,
|
|
3303
|
-
" "
|
|
3304
|
-
);
|
|
3305
|
-
try {
|
|
3306
|
-
const { stdout } = await (0, import_execa2.default)(pythonPath, ["-c", code]);
|
|
3307
|
-
const out = stdout.trim();
|
|
3308
|
-
return out || null;
|
|
3309
|
-
} catch (err) {
|
|
3310
|
-
(0, import_build_utils2.debug)("Failed to resolve Python user scripts directory", err);
|
|
3311
|
-
return null;
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
3688
|
async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
|
|
3315
|
-
const target = targetDir ? (0,
|
|
3689
|
+
const target = targetDir ? (0, import_path4.join)(targetDir, resolveVendorDir()) : resolveVendorDir();
|
|
3316
3690
|
process.env.PIP_USER = "0";
|
|
3317
3691
|
if (uvPath) {
|
|
3318
3692
|
const uvArgs = [
|
|
@@ -3325,111 +3699,38 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
|
|
|
3325
3699
|
...filterUnsafeUvPipArgs(args)
|
|
3326
3700
|
];
|
|
3327
3701
|
const prettyUv = `${uvPath} ${uvArgs.join(" ")}`;
|
|
3328
|
-
(0,
|
|
3702
|
+
(0, import_build_utils4.debug)(`Running "${prettyUv}"...`);
|
|
3329
3703
|
try {
|
|
3330
|
-
await (0,
|
|
3704
|
+
await (0, import_execa3.default)(uvPath, uvArgs, {
|
|
3331
3705
|
cwd: workPath,
|
|
3332
3706
|
env: getProtectedUvEnv()
|
|
3333
3707
|
});
|
|
3334
3708
|
return;
|
|
3335
3709
|
} catch (err) {
|
|
3336
3710
|
console.log(`Failed to run "${prettyUv}", falling back to pip`);
|
|
3337
|
-
(0,
|
|
3711
|
+
(0, import_build_utils4.debug)(`error: ${err}`);
|
|
3338
3712
|
}
|
|
3339
3713
|
}
|
|
3340
3714
|
const cmdArgs = [
|
|
3341
3715
|
"install",
|
|
3342
3716
|
"--disable-pip-version-check",
|
|
3343
3717
|
"--no-compile",
|
|
3344
|
-
"--no-cache-dir",
|
|
3345
|
-
"--target",
|
|
3346
|
-
target,
|
|
3347
|
-
...args
|
|
3348
|
-
];
|
|
3349
|
-
const pretty = `${pipPath} ${cmdArgs.join(" ")}`;
|
|
3350
|
-
(0,
|
|
3351
|
-
try {
|
|
3352
|
-
await (0,
|
|
3353
|
-
cwd: workPath
|
|
3354
|
-
});
|
|
3355
|
-
} catch (err) {
|
|
3356
|
-
console.log(`Failed to run "${pretty}"`);
|
|
3357
|
-
(0, import_build_utils2.debug)(`error: ${err}`);
|
|
3358
|
-
throw err;
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
async function maybeFindUvBin(pythonPath) {
|
|
3362
|
-
const found = import_which.default.sync("uv", { nothrow: true });
|
|
3363
|
-
if (found)
|
|
3364
|
-
return found;
|
|
3365
|
-
try {
|
|
3366
|
-
const globalScriptsDir = await getGlobalScriptsDir(pythonPath);
|
|
3367
|
-
if (globalScriptsDir) {
|
|
3368
|
-
const uvPath = (0, import_path2.join)(globalScriptsDir, uvExec);
|
|
3369
|
-
if (import_fs2.default.existsSync(uvPath))
|
|
3370
|
-
return uvPath;
|
|
3371
|
-
}
|
|
3372
|
-
} catch (err) {
|
|
3373
|
-
(0, import_build_utils2.debug)("Failed to resolve Python global scripts directory", err);
|
|
3374
|
-
}
|
|
3375
|
-
try {
|
|
3376
|
-
const userScriptsDir = await getUserScriptsDir(pythonPath);
|
|
3377
|
-
if (userScriptsDir) {
|
|
3378
|
-
const uvPath = (0, import_path2.join)(userScriptsDir, uvExec);
|
|
3379
|
-
if (import_fs2.default.existsSync(uvPath))
|
|
3380
|
-
return uvPath;
|
|
3381
|
-
}
|
|
3382
|
-
} catch (err) {
|
|
3383
|
-
(0, import_build_utils2.debug)("Failed to resolve Python user scripts directory", err);
|
|
3384
|
-
}
|
|
3385
|
-
try {
|
|
3386
|
-
const candidates = [];
|
|
3387
|
-
if (!isWin2) {
|
|
3388
|
-
candidates.push((0, import_path2.join)(import_os.default.homedir(), ".local", "bin", "uv"));
|
|
3389
|
-
candidates.push("/usr/local/bin/uv");
|
|
3390
|
-
candidates.push("/opt/homebrew/bin/uv");
|
|
3391
|
-
} else {
|
|
3392
|
-
candidates.push("C:\\Users\\Public\\uv\\uv.exe");
|
|
3393
|
-
}
|
|
3394
|
-
for (const p of candidates) {
|
|
3395
|
-
if (import_fs2.default.existsSync(p))
|
|
3396
|
-
return p;
|
|
3397
|
-
}
|
|
3398
|
-
} catch (err) {
|
|
3399
|
-
(0, import_build_utils2.debug)("Failed to resolve uv fallback paths", err);
|
|
3400
|
-
}
|
|
3401
|
-
return null;
|
|
3402
|
-
}
|
|
3403
|
-
async function getUvBinaryOrInstall(pythonPath) {
|
|
3404
|
-
const uvBin = await maybeFindUvBin(pythonPath);
|
|
3405
|
-
if (uvBin)
|
|
3406
|
-
return uvBin;
|
|
3407
|
-
try {
|
|
3408
|
-
console.log("Installing uv...");
|
|
3409
|
-
await (0, import_execa2.default)(
|
|
3410
|
-
pythonPath,
|
|
3411
|
-
[
|
|
3412
|
-
"-m",
|
|
3413
|
-
"pip",
|
|
3414
|
-
"install",
|
|
3415
|
-
"--disable-pip-version-check",
|
|
3416
|
-
"--no-cache-dir",
|
|
3417
|
-
"--user",
|
|
3418
|
-
"uv==0.8.18"
|
|
3419
|
-
],
|
|
3420
|
-
{ env: { ...process.env, PIP_USER: "1" } }
|
|
3421
|
-
);
|
|
3718
|
+
"--no-cache-dir",
|
|
3719
|
+
"--target",
|
|
3720
|
+
target,
|
|
3721
|
+
...args
|
|
3722
|
+
];
|
|
3723
|
+
const pretty = `${pipPath} ${cmdArgs.join(" ")}`;
|
|
3724
|
+
(0, import_build_utils4.debug)(`Running "${pretty}"...`);
|
|
3725
|
+
try {
|
|
3726
|
+
await (0, import_execa3.default)(pipPath, cmdArgs, {
|
|
3727
|
+
cwd: workPath
|
|
3728
|
+
});
|
|
3422
3729
|
} catch (err) {
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
}
|
|
3427
|
-
const resolvedUvBin = await maybeFindUvBin(pythonPath);
|
|
3428
|
-
if (!resolvedUvBin) {
|
|
3429
|
-
throw new Error("Unable to resolve uv binary after pip install");
|
|
3730
|
+
console.log(`Failed to run "${pretty}"`);
|
|
3731
|
+
(0, import_build_utils4.debug)(`error: ${err}`);
|
|
3732
|
+
throw err;
|
|
3430
3733
|
}
|
|
3431
|
-
console.log(`Installed uv at "${resolvedUvBin}"`);
|
|
3432
|
-
return resolvedUvBin;
|
|
3433
3734
|
}
|
|
3434
3735
|
async function installRequirement({
|
|
3435
3736
|
pythonPath,
|
|
@@ -3443,8 +3744,8 @@ async function installRequirement({
|
|
|
3443
3744
|
args = []
|
|
3444
3745
|
}) {
|
|
3445
3746
|
const actualTargetDir = targetDir || workPath;
|
|
3446
|
-
if (meta.isDev && await
|
|
3447
|
-
(0,
|
|
3747
|
+
if (meta.isDev && await isInstalled2(pythonPath, dependency, actualTargetDir)) {
|
|
3748
|
+
(0, import_build_utils4.debug)(
|
|
3448
3749
|
`Skipping ${dependency} dependency installation, already installed in ${actualTargetDir}`
|
|
3449
3750
|
);
|
|
3450
3751
|
return;
|
|
@@ -3464,7 +3765,7 @@ async function installRequirementsFile({
|
|
|
3464
3765
|
}) {
|
|
3465
3766
|
const actualTargetDir = targetDir || workPath;
|
|
3466
3767
|
if (meta.isDev && await areRequirementsInstalled(pythonPath, filePath, actualTargetDir)) {
|
|
3467
|
-
(0,
|
|
3768
|
+
(0, import_build_utils4.debug)(`Skipping requirements file installation, already installed`);
|
|
3468
3769
|
return;
|
|
3469
3770
|
}
|
|
3470
3771
|
await pipInstall(
|
|
@@ -3475,9 +3776,6 @@ async function installRequirementsFile({
|
|
|
3475
3776
|
targetDir
|
|
3476
3777
|
);
|
|
3477
3778
|
}
|
|
3478
|
-
function filterUnsafeUvPipArgs(args) {
|
|
3479
|
-
return args.filter((arg) => arg !== "--no-warn-script-location");
|
|
3480
|
-
}
|
|
3481
3779
|
async function exportRequirementsFromPipfile({
|
|
3482
3780
|
pythonPath,
|
|
3483
3781
|
pipPath,
|
|
@@ -3485,8 +3783,8 @@ async function exportRequirementsFromPipfile({
|
|
|
3485
3783
|
projectDir,
|
|
3486
3784
|
meta
|
|
3487
3785
|
}) {
|
|
3488
|
-
const tempDir = await
|
|
3489
|
-
(0,
|
|
3786
|
+
const tempDir = await import_fs3.default.promises.mkdtemp(
|
|
3787
|
+
(0, import_path4.join)(import_os2.default.tmpdir(), "vercel-pipenv-")
|
|
3490
3788
|
);
|
|
3491
3789
|
await installRequirement({
|
|
3492
3790
|
pythonPath,
|
|
@@ -3498,12 +3796,12 @@ async function exportRequirementsFromPipfile({
|
|
|
3498
3796
|
args: ["--no-warn-script-location"],
|
|
3499
3797
|
uvPath
|
|
3500
3798
|
});
|
|
3501
|
-
const tempVendorDir = (0,
|
|
3502
|
-
const convertCmd =
|
|
3503
|
-
(0,
|
|
3799
|
+
const tempVendorDir = (0, import_path4.join)(tempDir, resolveVendorDir());
|
|
3800
|
+
const convertCmd = isWin3 ? (0, import_path4.join)(tempVendorDir, "Scripts", "pipfile2req.exe") : (0, import_path4.join)(tempVendorDir, "bin", "pipfile2req");
|
|
3801
|
+
(0, import_build_utils4.debug)(`Running "${convertCmd}" in ${projectDir}...`);
|
|
3504
3802
|
let stdout;
|
|
3505
3803
|
try {
|
|
3506
|
-
const { stdout: out } = await (0,
|
|
3804
|
+
const { stdout: out } = await (0, import_execa3.default)(convertCmd, [], {
|
|
3507
3805
|
cwd: projectDir,
|
|
3508
3806
|
env: { ...process.env, PYTHONPATH: tempVendorDir }
|
|
3509
3807
|
});
|
|
@@ -3513,9 +3811,9 @@ async function exportRequirementsFromPipfile({
|
|
|
3513
3811
|
`Failed to run "${convertCmd}": ${err instanceof Error ? err.message : String(err)}`
|
|
3514
3812
|
);
|
|
3515
3813
|
}
|
|
3516
|
-
const outPath = (0,
|
|
3517
|
-
await
|
|
3518
|
-
(0,
|
|
3814
|
+
const outPath = (0, import_path4.join)(tempDir, "requirements.pipenv.txt");
|
|
3815
|
+
await import_fs3.default.promises.writeFile(outPath, stdout);
|
|
3816
|
+
(0, import_build_utils4.debug)(`Exported pipfile requirements to ${outPath}`);
|
|
3519
3817
|
return outPath;
|
|
3520
3818
|
}
|
|
3521
3819
|
async function mirrorSitePackagesIntoVendor({
|
|
@@ -3526,19 +3824,19 @@ async function mirrorSitePackagesIntoVendor({
|
|
|
3526
3824
|
try {
|
|
3527
3825
|
const sitePackageDirs = await getVenvSitePackagesDirs(venvPath);
|
|
3528
3826
|
for (const dir of sitePackageDirs) {
|
|
3529
|
-
if (!
|
|
3827
|
+
if (!import_fs3.default.existsSync(dir))
|
|
3530
3828
|
continue;
|
|
3531
|
-
const dirFiles = await (0,
|
|
3829
|
+
const dirFiles = await (0, import_build_utils4.glob)("**", dir);
|
|
3532
3830
|
for (const relativePath of Object.keys(dirFiles)) {
|
|
3533
3831
|
if (relativePath.endsWith(".pyc") || relativePath.includes("__pycache__")) {
|
|
3534
3832
|
continue;
|
|
3535
3833
|
}
|
|
3536
|
-
const srcFsPath = (0,
|
|
3537
|
-
const bundlePath = (0,
|
|
3834
|
+
const srcFsPath = (0, import_path4.join)(dir, relativePath);
|
|
3835
|
+
const bundlePath = (0, import_path4.join)(vendorDirName, relativePath).replace(
|
|
3538
3836
|
/\\/g,
|
|
3539
3837
|
"/"
|
|
3540
3838
|
);
|
|
3541
|
-
vendorFiles[bundlePath] = new
|
|
3839
|
+
vendorFiles[bundlePath] = new import_build_utils4.FileFsRef({ fsPath: srcFsPath });
|
|
3542
3840
|
}
|
|
3543
3841
|
}
|
|
3544
3842
|
} catch (err) {
|
|
@@ -3549,244 +3847,18 @@ async function mirrorSitePackagesIntoVendor({
|
|
|
3549
3847
|
}
|
|
3550
3848
|
|
|
3551
3849
|
// src/index.ts
|
|
3552
|
-
var
|
|
3553
|
-
|
|
3554
|
-
// src/version.ts
|
|
3555
|
-
var import_build_utils3 = require("@vercel/build-utils");
|
|
3556
|
-
var import_which2 = __toESM(require_lib());
|
|
3557
|
-
var DEFAULT_PYTHON_VERSION = "3.12";
|
|
3558
|
-
var allOptions = [
|
|
3559
|
-
{
|
|
3560
|
-
version: "3.14",
|
|
3561
|
-
pipPath: "pip3.14",
|
|
3562
|
-
pythonPath: "python3.14",
|
|
3563
|
-
runtime: "python3.14"
|
|
3564
|
-
},
|
|
3565
|
-
{
|
|
3566
|
-
version: "3.13",
|
|
3567
|
-
pipPath: "pip3.13",
|
|
3568
|
-
pythonPath: "python3.13",
|
|
3569
|
-
runtime: "python3.13"
|
|
3570
|
-
},
|
|
3571
|
-
{
|
|
3572
|
-
version: "3.12",
|
|
3573
|
-
pipPath: "pip3.12",
|
|
3574
|
-
pythonPath: "python3.12",
|
|
3575
|
-
runtime: "python3.12"
|
|
3576
|
-
},
|
|
3577
|
-
{
|
|
3578
|
-
version: "3.11",
|
|
3579
|
-
pipPath: "pip3.11",
|
|
3580
|
-
pythonPath: "python3.11",
|
|
3581
|
-
runtime: "python3.11"
|
|
3582
|
-
},
|
|
3583
|
-
{
|
|
3584
|
-
version: "3.10",
|
|
3585
|
-
pipPath: "pip3.10",
|
|
3586
|
-
pythonPath: "python3.10",
|
|
3587
|
-
runtime: "python3.10"
|
|
3588
|
-
},
|
|
3589
|
-
{
|
|
3590
|
-
version: "3.9",
|
|
3591
|
-
pipPath: "pip3.9",
|
|
3592
|
-
pythonPath: "python3.9",
|
|
3593
|
-
runtime: "python3.9"
|
|
3594
|
-
},
|
|
3595
|
-
{
|
|
3596
|
-
version: "3.6",
|
|
3597
|
-
pipPath: "pip3.6",
|
|
3598
|
-
pythonPath: "python3.6",
|
|
3599
|
-
runtime: "python3.6",
|
|
3600
|
-
discontinueDate: /* @__PURE__ */ new Date("2022-07-18")
|
|
3601
|
-
}
|
|
3602
|
-
];
|
|
3603
|
-
function getDevPythonVersion() {
|
|
3604
|
-
return {
|
|
3605
|
-
version: "3",
|
|
3606
|
-
pipPath: "pip3",
|
|
3607
|
-
pythonPath: "python3",
|
|
3608
|
-
runtime: "python3"
|
|
3609
|
-
};
|
|
3610
|
-
}
|
|
3611
|
-
function getLatestPythonVersion({
|
|
3612
|
-
isDev
|
|
3613
|
-
}) {
|
|
3614
|
-
if (isDev) {
|
|
3615
|
-
return getDevPythonVersion();
|
|
3616
|
-
}
|
|
3617
|
-
const defaultOption = allOptions.find(
|
|
3618
|
-
(opt) => opt.version === DEFAULT_PYTHON_VERSION
|
|
3619
|
-
);
|
|
3620
|
-
if (defaultOption && isInstalled2(defaultOption)) {
|
|
3621
|
-
return defaultOption;
|
|
3622
|
-
}
|
|
3623
|
-
const selection = allOptions.find(isInstalled2);
|
|
3624
|
-
if (!selection) {
|
|
3625
|
-
throw new import_build_utils3.NowBuildError({
|
|
3626
|
-
code: "PYTHON_NOT_FOUND",
|
|
3627
|
-
link: "https://vercel.link/python-version",
|
|
3628
|
-
message: `Unable to find any supported Python versions.`
|
|
3629
|
-
});
|
|
3630
|
-
}
|
|
3631
|
-
return selection;
|
|
3632
|
-
}
|
|
3633
|
-
function parseVersionTuple(input) {
|
|
3634
|
-
const cleaned = input.trim().replace(/\s+/g, "");
|
|
3635
|
-
const m = cleaned.match(/^(\d+)(?:\.(\d+))?/);
|
|
3636
|
-
if (!m)
|
|
3637
|
-
return null;
|
|
3638
|
-
const major = Number(m[1]);
|
|
3639
|
-
const minor = m[2] !== void 0 ? Number(m[2]) : 0;
|
|
3640
|
-
if (Number.isNaN(major) || Number.isNaN(minor))
|
|
3641
|
-
return null;
|
|
3642
|
-
return [major, minor];
|
|
3643
|
-
}
|
|
3644
|
-
function compareTuples(a, b) {
|
|
3645
|
-
if (a[0] !== b[0])
|
|
3646
|
-
return a[0] - b[0];
|
|
3647
|
-
return a[1] - b[1];
|
|
3648
|
-
}
|
|
3649
|
-
function parseSpecifier(spec) {
|
|
3650
|
-
const s = spec.trim();
|
|
3651
|
-
const m = s.match(/^(<=|>=|==|!=|~=|<|>)\s*([0-9]+(?:\.[0-9]+)?)(?:\.\*)?$/) || // Bare version like "3.11" -> implied ==
|
|
3652
|
-
s.match(/^()([0-9]+(?:\.[0-9]+)?)(?:\.\*)?$/);
|
|
3653
|
-
if (!m)
|
|
3654
|
-
return null;
|
|
3655
|
-
const op = m[1] || "==";
|
|
3656
|
-
const vt = parseVersionTuple(m[2]);
|
|
3657
|
-
if (!vt)
|
|
3658
|
-
return null;
|
|
3659
|
-
return { op, ver: vt };
|
|
3660
|
-
}
|
|
3661
|
-
function satisfies(candidate, spec) {
|
|
3662
|
-
const cmp = compareTuples(candidate, spec.ver);
|
|
3663
|
-
switch (spec.op) {
|
|
3664
|
-
case "==":
|
|
3665
|
-
return cmp === 0;
|
|
3666
|
-
case "!=":
|
|
3667
|
-
return cmp !== 0;
|
|
3668
|
-
case "<":
|
|
3669
|
-
return cmp < 0;
|
|
3670
|
-
case "<=":
|
|
3671
|
-
return cmp <= 0;
|
|
3672
|
-
case ">":
|
|
3673
|
-
return cmp > 0;
|
|
3674
|
-
case ">=":
|
|
3675
|
-
return cmp >= 0;
|
|
3676
|
-
case "~=": {
|
|
3677
|
-
const lowerOk = cmp >= 0;
|
|
3678
|
-
const upper = [spec.ver[0], spec.ver[1] + 1];
|
|
3679
|
-
return lowerOk && compareTuples(candidate, upper) < 0;
|
|
3680
|
-
}
|
|
3681
|
-
default:
|
|
3682
|
-
return false;
|
|
3683
|
-
}
|
|
3684
|
-
}
|
|
3685
|
-
function selectFromRequiresPython(expr) {
|
|
3686
|
-
const raw = expr.trim();
|
|
3687
|
-
if (!raw)
|
|
3688
|
-
return void 0;
|
|
3689
|
-
const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3690
|
-
const specifiers = [];
|
|
3691
|
-
for (const p of parts) {
|
|
3692
|
-
const sp = parseSpecifier(p);
|
|
3693
|
-
if (sp)
|
|
3694
|
-
specifiers.push(sp);
|
|
3695
|
-
}
|
|
3696
|
-
if (specifiers.length === 0) {
|
|
3697
|
-
return allOptions.find((o) => o.version === raw);
|
|
3698
|
-
}
|
|
3699
|
-
const matches = allOptions.filter((opt) => {
|
|
3700
|
-
const vt = parseVersionTuple(opt.version);
|
|
3701
|
-
return specifiers.every((sp) => satisfies(vt, sp));
|
|
3702
|
-
});
|
|
3703
|
-
if (matches.length === 0)
|
|
3704
|
-
return void 0;
|
|
3705
|
-
const installedMatch = matches.find(isInstalled2);
|
|
3706
|
-
return installedMatch ?? matches[0];
|
|
3707
|
-
}
|
|
3708
|
-
function getSupportedPythonVersion({
|
|
3709
|
-
isDev,
|
|
3710
|
-
declaredPythonVersion
|
|
3711
|
-
}) {
|
|
3712
|
-
if (isDev) {
|
|
3713
|
-
return getDevPythonVersion();
|
|
3714
|
-
}
|
|
3715
|
-
let selection = getLatestPythonVersion({ isDev: false });
|
|
3716
|
-
if (declaredPythonVersion) {
|
|
3717
|
-
const { version: version2, source } = declaredPythonVersion;
|
|
3718
|
-
let requested;
|
|
3719
|
-
if (source === "pyproject.toml") {
|
|
3720
|
-
requested = selectFromRequiresPython(version2);
|
|
3721
|
-
} else {
|
|
3722
|
-
requested = allOptions.find((o) => o.version === version2);
|
|
3723
|
-
}
|
|
3724
|
-
if (requested) {
|
|
3725
|
-
if (isDiscontinued(requested)) {
|
|
3726
|
-
throw new import_build_utils3.NowBuildError({
|
|
3727
|
-
code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
|
|
3728
|
-
link: "https://vercel.link/python-version",
|
|
3729
|
-
message: `Python version "${requested.version}" detected in ${source} is discontinued and must be upgraded.`
|
|
3730
|
-
});
|
|
3731
|
-
}
|
|
3732
|
-
if (isInstalled2(requested)) {
|
|
3733
|
-
selection = requested;
|
|
3734
|
-
console.log(`Using Python ${selection.version} from ${source}`);
|
|
3735
|
-
} else {
|
|
3736
|
-
console.warn(
|
|
3737
|
-
`Warning: Python version "${version2}" detected in ${source} is not installed and will be ignored. https://vercel.link/python-version`
|
|
3738
|
-
);
|
|
3739
|
-
console.log(
|
|
3740
|
-
`Falling back to latest installed version: ${selection.version}`
|
|
3741
|
-
);
|
|
3742
|
-
}
|
|
3743
|
-
} else {
|
|
3744
|
-
console.warn(
|
|
3745
|
-
`Warning: Python version "${version2}" detected in ${source} is invalid and will be ignored. https://vercel.link/python-version`
|
|
3746
|
-
);
|
|
3747
|
-
console.log(
|
|
3748
|
-
`Falling back to latest installed version: ${selection.version}`
|
|
3749
|
-
);
|
|
3750
|
-
}
|
|
3751
|
-
} else {
|
|
3752
|
-
console.log(
|
|
3753
|
-
`No Python version specified in pyproject.toml or Pipfile.lock. Using latest installed version: ${selection.version}`
|
|
3754
|
-
);
|
|
3755
|
-
}
|
|
3756
|
-
if (isDiscontinued(selection)) {
|
|
3757
|
-
throw new import_build_utils3.NowBuildError({
|
|
3758
|
-
code: "BUILD_UTILS_PYTHON_VERSION_DISCONTINUED",
|
|
3759
|
-
link: "https://vercel.link/python-version",
|
|
3760
|
-
message: `Python version "${selection.version}" declared in project configuration is discontinued and must be upgraded.`
|
|
3761
|
-
});
|
|
3762
|
-
}
|
|
3763
|
-
if (selection.discontinueDate) {
|
|
3764
|
-
const d = selection.discontinueDate.toISOString().split("T")[0];
|
|
3765
|
-
const srcSuffix = declaredPythonVersion ? `detected in ${declaredPythonVersion.source}` : "selected by runtime";
|
|
3766
|
-
console.warn(
|
|
3767
|
-
`Error: Python version "${selection.version}" ${srcSuffix} has reached End-of-Life. Deployments created on or after ${d} will fail to build. https://vercel.link/python-version`
|
|
3768
|
-
);
|
|
3769
|
-
}
|
|
3770
|
-
return selection;
|
|
3771
|
-
}
|
|
3772
|
-
function isDiscontinued({ discontinueDate }) {
|
|
3773
|
-
const today = Date.now();
|
|
3774
|
-
return discontinueDate !== void 0 && discontinueDate.getTime() <= today;
|
|
3775
|
-
}
|
|
3776
|
-
function isInstalled2({ pipPath, pythonPath }) {
|
|
3777
|
-
return Boolean(import_which2.default.sync(pipPath, { nothrow: true })) && Boolean(import_which2.default.sync(pythonPath, { nothrow: true }));
|
|
3778
|
-
}
|
|
3850
|
+
var import_build_utils9 = require("@vercel/build-utils");
|
|
3779
3851
|
|
|
3780
3852
|
// src/start-dev-server.ts
|
|
3781
|
-
var
|
|
3782
|
-
var
|
|
3783
|
-
var
|
|
3784
|
-
var
|
|
3853
|
+
var import_child_process2 = require("child_process");
|
|
3854
|
+
var import_fs4 = require("fs");
|
|
3855
|
+
var import_path6 = require("path");
|
|
3856
|
+
var import_build_utils7 = require("@vercel/build-utils");
|
|
3785
3857
|
|
|
3786
3858
|
// src/entrypoint.ts
|
|
3787
|
-
var
|
|
3788
|
-
var import_build_utils4 = require("@vercel/build-utils");
|
|
3859
|
+
var import_path5 = require("path");
|
|
3789
3860
|
var import_build_utils5 = require("@vercel/build-utils");
|
|
3861
|
+
var import_build_utils6 = require("@vercel/build-utils");
|
|
3790
3862
|
var PYTHON_ENTRYPOINT_FILENAMES = [
|
|
3791
3863
|
"app",
|
|
3792
3864
|
"index",
|
|
@@ -3798,11 +3870,11 @@ var PYTHON_ENTRYPOINT_FILENAMES = [
|
|
|
3798
3870
|
var PYTHON_ENTRYPOINT_DIRS = ["", "src", "app", "api"];
|
|
3799
3871
|
var PYTHON_CANDIDATE_ENTRYPOINTS = PYTHON_ENTRYPOINT_FILENAMES.flatMap(
|
|
3800
3872
|
(filename) => PYTHON_ENTRYPOINT_DIRS.map(
|
|
3801
|
-
(dir) =>
|
|
3873
|
+
(dir) => import_path5.posix.join(dir, `${filename}.py`)
|
|
3802
3874
|
)
|
|
3803
3875
|
);
|
|
3804
3876
|
async function getPyprojectEntrypoint(workPath) {
|
|
3805
|
-
const pyprojectData = await (0,
|
|
3877
|
+
const pyprojectData = await (0, import_build_utils6.readConfigFile)((0, import_path5.join)(workPath, "pyproject.toml"));
|
|
3806
3878
|
if (!pyprojectData)
|
|
3807
3879
|
return null;
|
|
3808
3880
|
const scripts = pyprojectData.project?.scripts;
|
|
@@ -3815,7 +3887,7 @@ async function getPyprojectEntrypoint(workPath) {
|
|
|
3815
3887
|
const modulePath = match[1];
|
|
3816
3888
|
const relPath = modulePath.replace(/\./g, "/");
|
|
3817
3889
|
try {
|
|
3818
|
-
const fsFiles = await (0,
|
|
3890
|
+
const fsFiles = await (0, import_build_utils5.glob)("**", workPath);
|
|
3819
3891
|
const candidates = [`${relPath}.py`, `${relPath}/__init__.py`];
|
|
3820
3892
|
for (const candidate of candidates) {
|
|
3821
3893
|
if (fsFiles[candidate])
|
|
@@ -3823,18 +3895,18 @@ async function getPyprojectEntrypoint(workPath) {
|
|
|
3823
3895
|
}
|
|
3824
3896
|
return null;
|
|
3825
3897
|
} catch {
|
|
3826
|
-
(0,
|
|
3898
|
+
(0, import_build_utils5.debug)("Failed to discover Python entrypoint from pyproject.toml");
|
|
3827
3899
|
return null;
|
|
3828
3900
|
}
|
|
3829
3901
|
}
|
|
3830
3902
|
async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
|
|
3831
3903
|
const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
|
|
3832
3904
|
try {
|
|
3833
|
-
const fsFiles = await (0,
|
|
3905
|
+
const fsFiles = await (0, import_build_utils5.glob)("**", workPath);
|
|
3834
3906
|
if (fsFiles[entry]) {
|
|
3835
|
-
const isValid = await (0,
|
|
3907
|
+
const isValid = await (0, import_build_utils5.isPythonEntrypoint)(fsFiles[entry]);
|
|
3836
3908
|
if (isValid) {
|
|
3837
|
-
(0,
|
|
3909
|
+
(0, import_build_utils5.debug)(`Using configured Python entrypoint: ${entry}`);
|
|
3838
3910
|
return entry;
|
|
3839
3911
|
}
|
|
3840
3912
|
}
|
|
@@ -3842,15 +3914,15 @@ async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
|
|
|
3842
3914
|
(c) => !!fsFiles[c]
|
|
3843
3915
|
);
|
|
3844
3916
|
for (const candidate of candidates) {
|
|
3845
|
-
const isValid = await (0,
|
|
3917
|
+
const isValid = await (0, import_build_utils5.isPythonEntrypoint)(fsFiles[candidate]);
|
|
3846
3918
|
if (isValid) {
|
|
3847
|
-
(0,
|
|
3919
|
+
(0, import_build_utils5.debug)(`Detected Python entrypoint: ${candidate}`);
|
|
3848
3920
|
return candidate;
|
|
3849
3921
|
}
|
|
3850
3922
|
}
|
|
3851
3923
|
return null;
|
|
3852
3924
|
} catch {
|
|
3853
|
-
(0,
|
|
3925
|
+
(0, import_build_utils5.debug)("Failed to discover Python entrypoint");
|
|
3854
3926
|
return null;
|
|
3855
3927
|
}
|
|
3856
3928
|
}
|
|
@@ -3907,12 +3979,12 @@ function installGlobalCleanupHandlers() {
|
|
|
3907
3979
|
try {
|
|
3908
3980
|
process.kill(info.pid, "SIGTERM");
|
|
3909
3981
|
} catch (err) {
|
|
3910
|
-
(0,
|
|
3982
|
+
(0, import_build_utils7.debug)(`Error sending SIGTERM to ${info.pid}: ${err}`);
|
|
3911
3983
|
}
|
|
3912
3984
|
try {
|
|
3913
3985
|
process.kill(info.pid, "SIGKILL");
|
|
3914
3986
|
} catch (err) {
|
|
3915
|
-
(0,
|
|
3987
|
+
(0, import_build_utils7.debug)(`Error sending SIGKILL to ${info.pid}: ${err}`);
|
|
3916
3988
|
}
|
|
3917
3989
|
PERSISTENT_SERVERS.delete(key);
|
|
3918
3990
|
}
|
|
@@ -3920,7 +3992,7 @@ function installGlobalCleanupHandlers() {
|
|
|
3920
3992
|
try {
|
|
3921
3993
|
restoreWarnings();
|
|
3922
3994
|
} catch (err) {
|
|
3923
|
-
(0,
|
|
3995
|
+
(0, import_build_utils7.debug)(`Error restoring warnings: ${err}`);
|
|
3924
3996
|
}
|
|
3925
3997
|
restoreWarnings = null;
|
|
3926
3998
|
}
|
|
@@ -3939,33 +4011,33 @@ function installGlobalCleanupHandlers() {
|
|
|
3939
4011
|
}
|
|
3940
4012
|
function createDevAsgiShim(workPath, modulePath) {
|
|
3941
4013
|
try {
|
|
3942
|
-
const vercelPythonDir = (0,
|
|
3943
|
-
(0,
|
|
3944
|
-
const shimPath = (0,
|
|
3945
|
-
const templatePath = (0,
|
|
3946
|
-
const template = (0,
|
|
4014
|
+
const vercelPythonDir = (0, import_path6.join)(workPath, ".vercel", "python");
|
|
4015
|
+
(0, import_fs4.mkdirSync)(vercelPythonDir, { recursive: true });
|
|
4016
|
+
const shimPath = (0, import_path6.join)(vercelPythonDir, `${ASGI_SHIM_MODULE}.py`);
|
|
4017
|
+
const templatePath = (0, import_path6.join)(__dirname, "..", `${ASGI_SHIM_MODULE}.py`);
|
|
4018
|
+
const template = (0, import_fs4.readFileSync)(templatePath, "utf8");
|
|
3947
4019
|
const shimSource = template.replace(/__VC_DEV_MODULE_PATH__/g, modulePath);
|
|
3948
|
-
(0,
|
|
3949
|
-
(0,
|
|
4020
|
+
(0, import_fs4.writeFileSync)(shimPath, shimSource, "utf8");
|
|
4021
|
+
(0, import_build_utils7.debug)(`Prepared Python dev static shim at ${shimPath}`);
|
|
3950
4022
|
return ASGI_SHIM_MODULE;
|
|
3951
4023
|
} catch (err) {
|
|
3952
|
-
(0,
|
|
4024
|
+
(0, import_build_utils7.debug)(`Failed to prepare dev static shim: ${err?.message || err}`);
|
|
3953
4025
|
return null;
|
|
3954
4026
|
}
|
|
3955
4027
|
}
|
|
3956
4028
|
function createDevWsgiShim(workPath, modulePath) {
|
|
3957
4029
|
try {
|
|
3958
|
-
const vercelPythonDir = (0,
|
|
3959
|
-
(0,
|
|
3960
|
-
const shimPath = (0,
|
|
3961
|
-
const templatePath = (0,
|
|
3962
|
-
const template = (0,
|
|
4030
|
+
const vercelPythonDir = (0, import_path6.join)(workPath, ".vercel", "python");
|
|
4031
|
+
(0, import_fs4.mkdirSync)(vercelPythonDir, { recursive: true });
|
|
4032
|
+
const shimPath = (0, import_path6.join)(vercelPythonDir, `${WSGI_SHIM_MODULE}.py`);
|
|
4033
|
+
const templatePath = (0, import_path6.join)(__dirname, "..", `${WSGI_SHIM_MODULE}.py`);
|
|
4034
|
+
const template = (0, import_fs4.readFileSync)(templatePath, "utf8");
|
|
3963
4035
|
const shimSource = template.replace(/__VC_DEV_MODULE_PATH__/g, modulePath);
|
|
3964
|
-
(0,
|
|
3965
|
-
(0,
|
|
4036
|
+
(0, import_fs4.writeFileSync)(shimPath, shimSource, "utf8");
|
|
4037
|
+
(0, import_build_utils7.debug)(`Prepared Python dev WSGI shim at ${shimPath}`);
|
|
3966
4038
|
return WSGI_SHIM_MODULE;
|
|
3967
4039
|
} catch (err) {
|
|
3968
|
-
(0,
|
|
4040
|
+
(0, import_build_utils7.debug)(`Failed to prepare dev WSGI shim: ${err?.message || err}`);
|
|
3969
4041
|
return null;
|
|
3970
4042
|
}
|
|
3971
4043
|
}
|
|
@@ -3985,7 +4057,7 @@ var startDevServer = async (opts) => {
|
|
|
3985
4057
|
);
|
|
3986
4058
|
if (!entry) {
|
|
3987
4059
|
const searched = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
|
|
3988
|
-
throw new
|
|
4060
|
+
throw new import_build_utils7.NowBuildError({
|
|
3989
4061
|
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
3990
4062
|
message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searched}.`,
|
|
3991
4063
|
link: `https://vercel.com/docs/frameworks/backend/${framework?.toLowerCase()}#exporting-the-${framework?.toLowerCase()}-application`,
|
|
@@ -4031,11 +4103,11 @@ var startDevServer = async (opts) => {
|
|
|
4031
4103
|
try {
|
|
4032
4104
|
await new Promise((resolve2, reject) => {
|
|
4033
4105
|
let resolved = false;
|
|
4034
|
-
const { pythonPath: systemPython } =
|
|
4106
|
+
const { pythonPath: systemPython } = getDefaultPythonVersion(meta);
|
|
4035
4107
|
let pythonCmd = systemPython;
|
|
4036
4108
|
const venv = isInVirtualEnv();
|
|
4037
4109
|
if (venv) {
|
|
4038
|
-
(0,
|
|
4110
|
+
(0, import_build_utils7.debug)(`Running in virtualenv at ${venv}`);
|
|
4039
4111
|
} else {
|
|
4040
4112
|
const { pythonCmd: venvPythonCmd, venvRoot } = useVirtualEnv(
|
|
4041
4113
|
workPath,
|
|
@@ -4044,9 +4116,9 @@ var startDevServer = async (opts) => {
|
|
|
4044
4116
|
);
|
|
4045
4117
|
pythonCmd = venvPythonCmd;
|
|
4046
4118
|
if (venvRoot) {
|
|
4047
|
-
(0,
|
|
4119
|
+
(0, import_build_utils7.debug)(`Using virtualenv at ${venvRoot}`);
|
|
4048
4120
|
} else {
|
|
4049
|
-
(0,
|
|
4121
|
+
(0, import_build_utils7.debug)("No virtualenv found");
|
|
4050
4122
|
try {
|
|
4051
4123
|
const yellow = "\x1B[33m";
|
|
4052
4124
|
const reset = "\x1B[0m";
|
|
@@ -4063,16 +4135,16 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4063
4135
|
if (framework !== "flask") {
|
|
4064
4136
|
const devShimModule = createDevAsgiShim(workPath, modulePath);
|
|
4065
4137
|
if (devShimModule) {
|
|
4066
|
-
const vercelPythonDir = (0,
|
|
4138
|
+
const vercelPythonDir = (0, import_path6.join)(workPath, ".vercel", "python");
|
|
4067
4139
|
const existingPythonPath = env.PYTHONPATH || "";
|
|
4068
4140
|
env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
|
|
4069
4141
|
}
|
|
4070
4142
|
const moduleToRun = devShimModule || modulePath;
|
|
4071
4143
|
const argv = ["-u", "-m", moduleToRun];
|
|
4072
|
-
(0,
|
|
4144
|
+
(0, import_build_utils7.debug)(
|
|
4073
4145
|
`Starting ASGI dev server (${framework}): ${pythonCmd} ${argv.join(" ")}`
|
|
4074
4146
|
);
|
|
4075
|
-
const child = (0,
|
|
4147
|
+
const child = (0, import_child_process2.spawn)(pythonCmd, argv, {
|
|
4076
4148
|
cwd: workPath,
|
|
4077
4149
|
env,
|
|
4078
4150
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -4143,14 +4215,14 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4143
4215
|
} else {
|
|
4144
4216
|
const devShimModule = createDevWsgiShim(workPath, modulePath);
|
|
4145
4217
|
if (devShimModule) {
|
|
4146
|
-
const vercelPythonDir = (0,
|
|
4218
|
+
const vercelPythonDir = (0, import_path6.join)(workPath, ".vercel", "python");
|
|
4147
4219
|
const existingPythonPath = env.PYTHONPATH || "";
|
|
4148
4220
|
env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
|
|
4149
4221
|
}
|
|
4150
4222
|
const moduleToRun = devShimModule || modulePath;
|
|
4151
4223
|
const argv = ["-u", "-m", moduleToRun];
|
|
4152
|
-
(0,
|
|
4153
|
-
const child = (0,
|
|
4224
|
+
(0, import_build_utils7.debug)(`Starting Flask dev server: ${pythonCmd} ${argv.join(" ")}`);
|
|
4225
|
+
const child = (0, import_child_process2.spawn)(pythonCmd, argv, {
|
|
4154
4226
|
cwd: workPath,
|
|
4155
4227
|
env,
|
|
4156
4228
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -4236,8 +4308,8 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4236
4308
|
};
|
|
4237
4309
|
|
|
4238
4310
|
// src/index.ts
|
|
4239
|
-
var readFile = (0, import_util.promisify)(
|
|
4240
|
-
var writeFile = (0, import_util.promisify)(
|
|
4311
|
+
var readFile = (0, import_util.promisify)(import_fs5.default.readFile);
|
|
4312
|
+
var writeFile = (0, import_util.promisify)(import_fs5.default.writeFile);
|
|
4241
4313
|
var version = 3;
|
|
4242
4314
|
async function downloadFilesInWorkPath({
|
|
4243
4315
|
entrypoint,
|
|
@@ -4245,13 +4317,13 @@ async function downloadFilesInWorkPath({
|
|
|
4245
4317
|
files,
|
|
4246
4318
|
meta = {}
|
|
4247
4319
|
}) {
|
|
4248
|
-
(0,
|
|
4249
|
-
let downloadedFiles = await (0,
|
|
4320
|
+
(0, import_build_utils8.debug)("Downloading user files...");
|
|
4321
|
+
let downloadedFiles = await (0, import_build_utils8.download)(files, workPath, meta);
|
|
4250
4322
|
if (meta.isDev) {
|
|
4251
|
-
const { devCacheDir = (0,
|
|
4252
|
-
const destCache = (0,
|
|
4253
|
-
await (0,
|
|
4254
|
-
downloadedFiles = await (0,
|
|
4323
|
+
const { devCacheDir = (0, import_path7.join)(workPath, ".now", "cache") } = meta;
|
|
4324
|
+
const destCache = (0, import_path7.join)(devCacheDir, (0, import_path7.basename)(entrypoint, ".py"));
|
|
4325
|
+
await (0, import_build_utils8.download)(downloadedFiles, destCache);
|
|
4326
|
+
downloadedFiles = await (0, import_build_utils8.glob)("**", destCache);
|
|
4255
4327
|
workPath = destCache;
|
|
4256
4328
|
}
|
|
4257
4329
|
return workPath;
|
|
@@ -4275,21 +4347,21 @@ var build = async ({
|
|
|
4275
4347
|
});
|
|
4276
4348
|
try {
|
|
4277
4349
|
if (meta.isDev) {
|
|
4278
|
-
const setupCfg = (0,
|
|
4350
|
+
const setupCfg = (0, import_path7.join)(workPath, "setup.cfg");
|
|
4279
4351
|
await writeFile(setupCfg, "[install]\nprefix=\n");
|
|
4280
4352
|
}
|
|
4281
4353
|
} catch (err) {
|
|
4282
4354
|
console.log('Failed to create "setup.cfg" file');
|
|
4283
4355
|
throw err;
|
|
4284
4356
|
}
|
|
4285
|
-
if ((0,
|
|
4357
|
+
if ((0, import_build_utils8.isPythonFramework)(framework)) {
|
|
4286
4358
|
const {
|
|
4287
4359
|
cliType,
|
|
4288
4360
|
lockfileVersion,
|
|
4289
4361
|
packageJsonPackageManager,
|
|
4290
4362
|
turboSupportsCorepackHome
|
|
4291
|
-
} = await (0,
|
|
4292
|
-
spawnEnv = (0,
|
|
4363
|
+
} = await (0, import_build_utils8.scanParentDirs)(workPath, true);
|
|
4364
|
+
spawnEnv = (0, import_build_utils8.getEnvForPackageManager)({
|
|
4293
4365
|
cliType,
|
|
4294
4366
|
lockfileVersion,
|
|
4295
4367
|
packageJsonPackageManager,
|
|
@@ -4310,7 +4382,7 @@ var build = async ({
|
|
|
4310
4382
|
config?.buildCommand;
|
|
4311
4383
|
if (projectBuildCommand) {
|
|
4312
4384
|
console.log(`Running "${projectBuildCommand}"`);
|
|
4313
|
-
await (0,
|
|
4385
|
+
await (0, import_build_utils8.execCommand)(projectBuildCommand, {
|
|
4314
4386
|
env: spawnEnv,
|
|
4315
4387
|
cwd: workPath
|
|
4316
4388
|
});
|
|
@@ -4322,21 +4394,21 @@ var build = async ({
|
|
|
4322
4394
|
);
|
|
4323
4395
|
}
|
|
4324
4396
|
}
|
|
4325
|
-
let fsFiles = await (0,
|
|
4326
|
-
if ((0,
|
|
4397
|
+
let fsFiles = await (0, import_build_utils8.glob)("**", workPath);
|
|
4398
|
+
if ((0, import_build_utils8.isPythonFramework)(framework) && (!fsFiles[entrypoint] || !entrypoint.endsWith(".py"))) {
|
|
4327
4399
|
const detected = await detectPythonEntrypoint(
|
|
4328
4400
|
config.framework,
|
|
4329
4401
|
workPath,
|
|
4330
4402
|
entrypoint
|
|
4331
4403
|
);
|
|
4332
4404
|
if (detected) {
|
|
4333
|
-
(0,
|
|
4405
|
+
(0, import_build_utils8.debug)(
|
|
4334
4406
|
`Resolved Python entrypoint to "${detected}" (configured "${entrypoint}" not found).`
|
|
4335
4407
|
);
|
|
4336
4408
|
entrypoint = detected;
|
|
4337
4409
|
} else {
|
|
4338
4410
|
const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
|
|
4339
|
-
throw new
|
|
4411
|
+
throw new import_build_utils8.NowBuildError({
|
|
4340
4412
|
code: `${framework.toUpperCase()}_ENTRYPOINT_NOT_FOUND`,
|
|
4341
4413
|
message: `No ${framework} entrypoint found. Add an 'app' script in pyproject.toml or define an entrypoint in one of: ${searchedList}.`,
|
|
4342
4414
|
link: `https://vercel.com/docs/frameworks/backend/${framework}#exporting-the-${framework}-application`,
|
|
@@ -4344,7 +4416,7 @@ var build = async ({
|
|
|
4344
4416
|
});
|
|
4345
4417
|
}
|
|
4346
4418
|
}
|
|
4347
|
-
const entryDirectory = (0,
|
|
4419
|
+
const entryDirectory = (0, import_path7.dirname)(entrypoint);
|
|
4348
4420
|
const pyprojectDir = findDir({
|
|
4349
4421
|
file: "pyproject.toml",
|
|
4350
4422
|
entryDirectory,
|
|
@@ -4357,29 +4429,51 @@ var build = async ({
|
|
|
4357
4429
|
workPath,
|
|
4358
4430
|
fsFiles
|
|
4359
4431
|
});
|
|
4432
|
+
const pythonVersionFileDir = findDir({
|
|
4433
|
+
file: ".python-version",
|
|
4434
|
+
entryDirectory,
|
|
4435
|
+
workPath,
|
|
4436
|
+
fsFiles
|
|
4437
|
+
});
|
|
4360
4438
|
let declaredPythonVersion;
|
|
4361
|
-
if (
|
|
4439
|
+
if (pythonVersionFileDir) {
|
|
4440
|
+
try {
|
|
4441
|
+
const content = await readFile(
|
|
4442
|
+
(0, import_path7.join)(pythonVersionFileDir, ".python-version"),
|
|
4443
|
+
"utf8"
|
|
4444
|
+
);
|
|
4445
|
+
const version2 = parsePythonVersionFile(content);
|
|
4446
|
+
if (version2) {
|
|
4447
|
+
declaredPythonVersion = { version: version2, source: ".python-version" };
|
|
4448
|
+
(0, import_build_utils8.debug)(`Found Python version ${version2} in .python-version`);
|
|
4449
|
+
}
|
|
4450
|
+
} catch (err) {
|
|
4451
|
+
(0, import_build_utils8.debug)("Failed to read .python-version file", err);
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4454
|
+
if (!declaredPythonVersion && pyprojectDir) {
|
|
4362
4455
|
let requiresPython;
|
|
4363
4456
|
try {
|
|
4364
|
-
const pyproject = await (0,
|
|
4457
|
+
const pyproject = await (0, import_build_utils9.readConfigFile)((0, import_path7.join)(pyprojectDir, "pyproject.toml"));
|
|
4365
4458
|
requiresPython = pyproject?.project?.["requires-python"];
|
|
4366
4459
|
} catch (err) {
|
|
4367
|
-
(0,
|
|
4460
|
+
(0, import_build_utils8.debug)("Failed to parse pyproject.toml", err);
|
|
4368
4461
|
}
|
|
4369
4462
|
if (typeof requiresPython === "string" && requiresPython.trim()) {
|
|
4370
4463
|
declaredPythonVersion = {
|
|
4371
4464
|
version: requiresPython.trim(),
|
|
4372
4465
|
source: "pyproject.toml"
|
|
4373
4466
|
};
|
|
4374
|
-
(0,
|
|
4467
|
+
(0, import_build_utils8.debug)(`Found requires-python "${requiresPython}" in pyproject.toml`);
|
|
4375
4468
|
}
|
|
4376
|
-
}
|
|
4469
|
+
}
|
|
4470
|
+
if (!declaredPythonVersion && pipfileLockDir) {
|
|
4377
4471
|
let lock = {};
|
|
4378
4472
|
try {
|
|
4379
|
-
const json = await readFile((0,
|
|
4473
|
+
const json = await readFile((0, import_path7.join)(pipfileLockDir, "Pipfile.lock"), "utf8");
|
|
4380
4474
|
lock = JSON.parse(json);
|
|
4381
4475
|
} catch (err) {
|
|
4382
|
-
throw new
|
|
4476
|
+
throw new import_build_utils8.NowBuildError({
|
|
4383
4477
|
code: "INVALID_PIPFILE_LOCK",
|
|
4384
4478
|
message: "Unable to parse Pipfile.lock"
|
|
4385
4479
|
});
|
|
@@ -4387,20 +4481,30 @@ var build = async ({
|
|
|
4387
4481
|
const pyFromLock = lock?._meta?.requires?.python_version;
|
|
4388
4482
|
if (pyFromLock) {
|
|
4389
4483
|
declaredPythonVersion = { version: pyFromLock, source: "Pipfile.lock" };
|
|
4390
|
-
(0,
|
|
4484
|
+
(0, import_build_utils8.debug)(`Found Python version ${pyFromLock} in Pipfile.lock`);
|
|
4391
4485
|
}
|
|
4392
4486
|
}
|
|
4393
4487
|
const pythonVersion = getSupportedPythonVersion({
|
|
4394
4488
|
isDev: meta.isDev,
|
|
4395
4489
|
declaredPythonVersion
|
|
4396
4490
|
});
|
|
4397
|
-
|
|
4398
|
-
const
|
|
4491
|
+
const selectedVersionTuple = parseVersionTuple(pythonVersion.version);
|
|
4492
|
+
const defaultVersionTuple = parseVersionTuple(DEFAULT_PYTHON_VERSION);
|
|
4493
|
+
if (!pythonVersionFileDir && pyprojectDir && declaredPythonVersion?.source === "pyproject.toml" && selectedVersionTuple && defaultVersionTuple && compareTuples(selectedVersionTuple, defaultVersionTuple) <= 0) {
|
|
4494
|
+
const pythonVersionFilePath = (0, import_path7.join)(pyprojectDir, ".python-version");
|
|
4495
|
+
await writeFile(pythonVersionFilePath, `${pythonVersion.version}
|
|
4496
|
+
`);
|
|
4497
|
+
console.log(
|
|
4498
|
+
`Writing .python-version file with version ${pythonVersion.version}`
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
fsFiles = await (0, import_build_utils8.glob)("**", workPath);
|
|
4502
|
+
const venvPath = (0, import_path7.join)(workPath, ".vercel", "python", ".venv");
|
|
4399
4503
|
await ensureVenv({
|
|
4400
4504
|
pythonPath: pythonVersion.pythonPath,
|
|
4401
4505
|
venvPath
|
|
4402
4506
|
});
|
|
4403
|
-
const hasCustomInstallCommand = (0,
|
|
4507
|
+
const hasCustomInstallCommand = (0, import_build_utils8.isPythonFramework)(framework) && !!projectInstallCommand;
|
|
4404
4508
|
let useRuntime = false;
|
|
4405
4509
|
if (hasCustomInstallCommand) {
|
|
4406
4510
|
const baseEnv = spawnEnv || process.env;
|
|
@@ -4408,13 +4512,13 @@ var build = async ({
|
|
|
4408
4512
|
pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
|
|
4409
4513
|
const installCommand = projectInstallCommand;
|
|
4410
4514
|
console.log(`Running "install" command: \`${installCommand}\`...`);
|
|
4411
|
-
await (0,
|
|
4515
|
+
await (0, import_build_utils8.execCommand)(installCommand, {
|
|
4412
4516
|
env: pythonEnv,
|
|
4413
4517
|
cwd: workPath
|
|
4414
4518
|
});
|
|
4415
4519
|
} else {
|
|
4416
4520
|
let ranPyprojectInstall = false;
|
|
4417
|
-
if ((0,
|
|
4521
|
+
if ((0, import_build_utils8.isPythonFramework)(framework)) {
|
|
4418
4522
|
const baseEnv = spawnEnv || process.env;
|
|
4419
4523
|
const pythonEnv = createVenvEnv(venvPath, baseEnv);
|
|
4420
4524
|
pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
|
|
@@ -4427,10 +4531,11 @@ var build = async ({
|
|
|
4427
4531
|
);
|
|
4428
4532
|
}
|
|
4429
4533
|
if (!ranPyprojectInstall) {
|
|
4430
|
-
let
|
|
4534
|
+
let uv;
|
|
4431
4535
|
try {
|
|
4432
|
-
uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
|
|
4536
|
+
const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
|
|
4433
4537
|
console.log(`Using uv at "${uvPath}"`);
|
|
4538
|
+
uv = new UvRunner(uvPath);
|
|
4434
4539
|
} catch (err) {
|
|
4435
4540
|
console.log("Failed to install or locate uv");
|
|
4436
4541
|
throw new Error(
|
|
@@ -4456,25 +4561,24 @@ var build = async ({
|
|
|
4456
4561
|
repoRootPath,
|
|
4457
4562
|
pythonPath: pythonVersion.pythonPath,
|
|
4458
4563
|
pipPath: pythonVersion.pipPath,
|
|
4459
|
-
|
|
4564
|
+
uv,
|
|
4460
4565
|
venvPath,
|
|
4461
4566
|
meta,
|
|
4462
4567
|
runtimeDependencies
|
|
4463
4568
|
});
|
|
4464
|
-
await
|
|
4465
|
-
uvPath,
|
|
4569
|
+
await uv.sync({
|
|
4466
4570
|
venvPath,
|
|
4467
4571
|
projectDir,
|
|
4468
4572
|
locked: true
|
|
4469
4573
|
});
|
|
4470
4574
|
}
|
|
4471
4575
|
}
|
|
4472
|
-
(0,
|
|
4576
|
+
(0, import_build_utils8.debug)("Entrypoint is", entrypoint);
|
|
4473
4577
|
const moduleName = entrypoint.replace(/\//g, ".").replace(/\.py$/i, "");
|
|
4474
4578
|
const vendorDir = resolveVendorDir();
|
|
4475
4579
|
const suffix = meta.isDev && !entrypoint.endsWith(".py") ? ".py" : "";
|
|
4476
4580
|
const entrypointWithSuffix = `${entrypoint}${suffix}`;
|
|
4477
|
-
(0,
|
|
4581
|
+
(0, import_build_utils8.debug)("Entrypoint with suffix is", entrypointWithSuffix);
|
|
4478
4582
|
let handlerPyContents;
|
|
4479
4583
|
if (useRuntime) {
|
|
4480
4584
|
handlerPyContents = `
|
|
@@ -4516,7 +4620,7 @@ if os.path.isdir(_vendor):
|
|
|
4516
4620
|
from vercel_runtime.vc_init import vc_handler
|
|
4517
4621
|
`;
|
|
4518
4622
|
} else {
|
|
4519
|
-
const originalPyPath = (0,
|
|
4623
|
+
const originalPyPath = (0, import_path7.join)(__dirname, "..", "vc_init.py");
|
|
4520
4624
|
const originalHandlerPyContents = await readFile(originalPyPath, "utf8");
|
|
4521
4625
|
handlerPyContents = originalHandlerPyContents.replace(/__VC_HANDLER_MODULE_NAME/g, moduleName).replace(/__VC_HANDLER_ENTRYPOINT/g, entrypointWithSuffix).replace(/__VC_HANDLER_VENDOR_DIR/g, vendorDir);
|
|
4522
4626
|
}
|
|
@@ -4544,7 +4648,7 @@ from vercel_runtime.vc_init import vc_handler
|
|
|
4544
4648
|
cwd: workPath,
|
|
4545
4649
|
ignore: config && typeof config.excludeFiles === "string" ? [...predefinedExcludes, config.excludeFiles] : predefinedExcludes
|
|
4546
4650
|
};
|
|
4547
|
-
const files = await (0,
|
|
4651
|
+
const files = await (0, import_build_utils8.glob)("**", globOptions);
|
|
4548
4652
|
const vendorFiles = await mirrorSitePackagesIntoVendor({
|
|
4549
4653
|
venvPath,
|
|
4550
4654
|
vendorDirName: vendorDir
|
|
@@ -4553,12 +4657,12 @@ from vercel_runtime.vc_init import vc_handler
|
|
|
4553
4657
|
files[p] = f;
|
|
4554
4658
|
}
|
|
4555
4659
|
const handlerPyFilename = "vc__handler__python";
|
|
4556
|
-
files[`${handlerPyFilename}.py`] = new
|
|
4660
|
+
files[`${handlerPyFilename}.py`] = new import_build_utils8.FileBlob({ data: handlerPyContents });
|
|
4557
4661
|
if (config.framework === "fasthtml") {
|
|
4558
4662
|
const { SESSKEY = "" } = process.env;
|
|
4559
|
-
files[".sesskey"] = new
|
|
4663
|
+
files[".sesskey"] = new import_build_utils8.FileBlob({ data: `"${SESSKEY}"` });
|
|
4560
4664
|
}
|
|
4561
|
-
const output = new
|
|
4665
|
+
const output = new import_build_utils8.Lambda({
|
|
4562
4666
|
files,
|
|
4563
4667
|
handler: `${handlerPyFilename}.vc_handler`,
|
|
4564
4668
|
runtime: pythonVersion.runtime,
|
|
@@ -4594,7 +4698,7 @@ var defaultShouldServe = ({
|
|
|
4594
4698
|
if (entrypoint === requestPath && hasProp(files, entrypoint)) {
|
|
4595
4699
|
return true;
|
|
4596
4700
|
}
|
|
4597
|
-
const { dir, name } = (0,
|
|
4701
|
+
const { dir, name } = (0, import_path7.parse)(entrypoint);
|
|
4598
4702
|
if (name === "index" && dir === requestPath && hasProp(files, entrypoint)) {
|
|
4599
4703
|
return true;
|
|
4600
4704
|
}
|
|
@@ -4603,6 +4707,16 @@ var defaultShouldServe = ({
|
|
|
4603
4707
|
function hasProp(obj, key) {
|
|
4604
4708
|
return Object.hasOwnProperty.call(obj, key);
|
|
4605
4709
|
}
|
|
4710
|
+
function parsePythonVersionFile(content) {
|
|
4711
|
+
const lines = content.split("\n");
|
|
4712
|
+
for (const line of lines) {
|
|
4713
|
+
const trimmed = line.trim();
|
|
4714
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
4715
|
+
continue;
|
|
4716
|
+
return trimmed;
|
|
4717
|
+
}
|
|
4718
|
+
return void 0;
|
|
4719
|
+
}
|
|
4606
4720
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4607
4721
|
0 && (module.exports = {
|
|
4608
4722
|
build,
|