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