@vercel/python 6.9.0 → 6.11.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 +197 -290
- package/package.json +5 -2
- package/vc_init_dev_asgi.py +12 -2
- package/vc_init_dev_wsgi.py +1 -1
package/dist/index.js
CHANGED
|
@@ -137,12 +137,12 @@ var require_isexe = __commonJS({
|
|
|
137
137
|
if (typeof Promise !== "function") {
|
|
138
138
|
throw new TypeError("callback not provided");
|
|
139
139
|
}
|
|
140
|
-
return new Promise(function(
|
|
140
|
+
return new Promise(function(resolve, reject) {
|
|
141
141
|
isexe(path, options || {}, function(er, is) {
|
|
142
142
|
if (er) {
|
|
143
143
|
reject(er);
|
|
144
144
|
} else {
|
|
145
|
-
|
|
145
|
+
resolve(is);
|
|
146
146
|
}
|
|
147
147
|
});
|
|
148
148
|
});
|
|
@@ -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 isWin3 = process.platform === "win32";
|
|
1502
1502
|
var isExecutableRegExp = /\.(?:com|exe)$/i;
|
|
1503
1503
|
var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
|
|
1504
1504
|
var supportsShellOption = niceTry(() => semver.satisfies(process.version, "^4.8.0 || ^5.7.0 || >= 6.0.0", true)) || false;
|
|
@@ -1513,7 +1513,7 @@ var require_parse = __commonJS({
|
|
|
1513
1513
|
return parsed.file;
|
|
1514
1514
|
}
|
|
1515
1515
|
function parseNonShell(parsed) {
|
|
1516
|
-
if (!
|
|
1516
|
+
if (!isWin3) {
|
|
1517
1517
|
return parsed;
|
|
1518
1518
|
}
|
|
1519
1519
|
const commandFile = detectShebang(parsed);
|
|
@@ -1535,7 +1535,7 @@ var require_parse = __commonJS({
|
|
|
1535
1535
|
return parsed;
|
|
1536
1536
|
}
|
|
1537
1537
|
const shellCommand = [parsed.command].concat(parsed.args).join(" ");
|
|
1538
|
-
if (
|
|
1538
|
+
if (isWin3) {
|
|
1539
1539
|
parsed.command = typeof parsed.options.shell === "string" ? parsed.options.shell : process.env.comspec || "cmd.exe";
|
|
1540
1540
|
parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`];
|
|
1541
1541
|
parsed.options.windowsVerbatimArguments = true;
|
|
@@ -1578,7 +1578,7 @@ var require_parse = __commonJS({
|
|
|
1578
1578
|
var require_enoent = __commonJS({
|
|
1579
1579
|
"../../node_modules/.pnpm/cross-spawn@6.0.5/node_modules/cross-spawn/lib/enoent.js"(exports, module2) {
|
|
1580
1580
|
"use strict";
|
|
1581
|
-
var
|
|
1581
|
+
var isWin3 = process.platform === "win32";
|
|
1582
1582
|
function notFoundError(original, syscall) {
|
|
1583
1583
|
return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
|
|
1584
1584
|
code: "ENOENT",
|
|
@@ -1589,7 +1589,7 @@ var require_enoent = __commonJS({
|
|
|
1589
1589
|
});
|
|
1590
1590
|
}
|
|
1591
1591
|
function hookChildProcess(cp, parsed) {
|
|
1592
|
-
if (!
|
|
1592
|
+
if (!isWin3) {
|
|
1593
1593
|
return;
|
|
1594
1594
|
}
|
|
1595
1595
|
const originalEmit = cp.emit;
|
|
@@ -1604,13 +1604,13 @@ var require_enoent = __commonJS({
|
|
|
1604
1604
|
};
|
|
1605
1605
|
}
|
|
1606
1606
|
function verifyENOENT(status, parsed) {
|
|
1607
|
-
if (
|
|
1607
|
+
if (isWin3 && status === 1 && !parsed.file) {
|
|
1608
1608
|
return notFoundError(parsed.original, "spawn");
|
|
1609
1609
|
}
|
|
1610
1610
|
return null;
|
|
1611
1611
|
}
|
|
1612
1612
|
function verifyENOENTSync(status, parsed) {
|
|
1613
|
-
if (
|
|
1613
|
+
if (isWin3 && status === 1 && !parsed.file) {
|
|
1614
1614
|
return notFoundError(parsed.original, "spawnSync");
|
|
1615
1615
|
}
|
|
1616
1616
|
return null;
|
|
@@ -2042,7 +2042,7 @@ var require_get_stream = __commonJS({
|
|
|
2042
2042
|
options = Object.assign({ maxBuffer: Infinity }, options);
|
|
2043
2043
|
const { maxBuffer } = options;
|
|
2044
2044
|
let stream;
|
|
2045
|
-
return new Promise((
|
|
2045
|
+
return new Promise((resolve, reject) => {
|
|
2046
2046
|
const rejectPromise = (error) => {
|
|
2047
2047
|
if (error) {
|
|
2048
2048
|
error.bufferedData = stream.getBufferedValue();
|
|
@@ -2054,7 +2054,7 @@ var require_get_stream = __commonJS({
|
|
|
2054
2054
|
rejectPromise(error);
|
|
2055
2055
|
return;
|
|
2056
2056
|
}
|
|
2057
|
-
|
|
2057
|
+
resolve();
|
|
2058
2058
|
});
|
|
2059
2059
|
stream.on("data", () => {
|
|
2060
2060
|
if (stream.getBufferedLength() > maxBuffer) {
|
|
@@ -2078,11 +2078,11 @@ var require_p_finally = __commonJS({
|
|
|
2078
2078
|
onFinally = onFinally || (() => {
|
|
2079
2079
|
});
|
|
2080
2080
|
return promise.then(
|
|
2081
|
-
(val) => new Promise((
|
|
2082
|
-
|
|
2081
|
+
(val) => new Promise((resolve) => {
|
|
2082
|
+
resolve(onFinally());
|
|
2083
2083
|
}).then(() => val),
|
|
2084
|
-
(err) => new Promise((
|
|
2085
|
-
|
|
2084
|
+
(err) => new Promise((resolve) => {
|
|
2085
|
+
resolve(onFinally());
|
|
2086
2086
|
}).then(() => {
|
|
2087
2087
|
throw err;
|
|
2088
2088
|
})
|
|
@@ -2143,7 +2143,7 @@ var require_signal_exit = __commonJS({
|
|
|
2143
2143
|
} else {
|
|
2144
2144
|
assert = require("assert");
|
|
2145
2145
|
signals = require_signals();
|
|
2146
|
-
|
|
2146
|
+
isWin3 = /^win/i.test(process2.platform);
|
|
2147
2147
|
EE = require("events");
|
|
2148
2148
|
if (typeof EE !== "function") {
|
|
2149
2149
|
EE = EE.EventEmitter;
|
|
@@ -2215,7 +2215,7 @@ var require_signal_exit = __commonJS({
|
|
|
2215
2215
|
unload();
|
|
2216
2216
|
emit("exit", null, sig);
|
|
2217
2217
|
emit("afterexit", null, sig);
|
|
2218
|
-
if (
|
|
2218
|
+
if (isWin3 && sig === "SIGHUP") {
|
|
2219
2219
|
sig = "SIGINT";
|
|
2220
2220
|
}
|
|
2221
2221
|
process2.kill(process2.pid, sig);
|
|
@@ -2272,7 +2272,7 @@ var require_signal_exit = __commonJS({
|
|
|
2272
2272
|
}
|
|
2273
2273
|
var assert;
|
|
2274
2274
|
var signals;
|
|
2275
|
-
var
|
|
2275
|
+
var isWin3;
|
|
2276
2276
|
var EE;
|
|
2277
2277
|
var emitter;
|
|
2278
2278
|
var unload;
|
|
@@ -2461,8 +2461,8 @@ var require_execa = __commonJS({
|
|
|
2461
2461
|
}
|
|
2462
2462
|
let ret;
|
|
2463
2463
|
if (!buffer) {
|
|
2464
|
-
ret = new Promise((
|
|
2465
|
-
process2[stream].once("end",
|
|
2464
|
+
ret = new Promise((resolve, reject) => {
|
|
2465
|
+
process2[stream].once("end", resolve).once("error", reject);
|
|
2466
2466
|
});
|
|
2467
2467
|
} else if (encoding) {
|
|
2468
2468
|
ret = _getStream(process2[stream], {
|
|
@@ -2551,19 +2551,19 @@ ${stderr}${stdout}`;
|
|
|
2551
2551
|
spawned.kill(parsed.opts.killSignal);
|
|
2552
2552
|
}, parsed.opts.timeout);
|
|
2553
2553
|
}
|
|
2554
|
-
const processDone = new Promise((
|
|
2554
|
+
const processDone = new Promise((resolve) => {
|
|
2555
2555
|
spawned.on("exit", (code, signal) => {
|
|
2556
2556
|
cleanup();
|
|
2557
|
-
|
|
2557
|
+
resolve({ code, signal });
|
|
2558
2558
|
});
|
|
2559
2559
|
spawned.on("error", (err) => {
|
|
2560
2560
|
cleanup();
|
|
2561
|
-
|
|
2561
|
+
resolve({ error: err });
|
|
2562
2562
|
});
|
|
2563
2563
|
if (spawned.stdin) {
|
|
2564
2564
|
spawned.stdin.on("error", (err) => {
|
|
2565
2565
|
cleanup();
|
|
2566
|
-
|
|
2566
|
+
resolve({ error: err });
|
|
2567
2567
|
});
|
|
2568
2568
|
}
|
|
2569
2569
|
});
|
|
@@ -2651,7 +2651,7 @@ ${stderr}${stdout}`;
|
|
|
2651
2651
|
var require_lib = __commonJS({
|
|
2652
2652
|
"../../node_modules/.pnpm/which@3.0.0/node_modules/which/lib/index.js"(exports, module2) {
|
|
2653
2653
|
var isexe = require_isexe();
|
|
2654
|
-
var { join: join7, delimiter, sep, posix } = require("path");
|
|
2654
|
+
var { join: join7, delimiter: delimiter2, 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}`);
|
|
@@ -2659,7 +2659,7 @@ var require_lib = __commonJS({
|
|
|
2659
2659
|
var getPathInfo = (cmd, {
|
|
2660
2660
|
path: optPath = process.env.PATH,
|
|
2661
2661
|
pathExt: optPathExt = process.env.PATHEXT,
|
|
2662
|
-
delimiter: optDelimiter =
|
|
2662
|
+
delimiter: optDelimiter = delimiter2
|
|
2663
2663
|
}) => {
|
|
2664
2664
|
const pathEnv = cmd.match(rSlash) ? [""] : [
|
|
2665
2665
|
// windows always checks the cwd first
|
|
@@ -2761,9 +2761,9 @@ var import_build_utils8 = require("@vercel/build-utils");
|
|
|
2761
2761
|
// src/install.ts
|
|
2762
2762
|
var import_execa3 = __toESM(require_execa());
|
|
2763
2763
|
var import_fs3 = __toESM(require("fs"));
|
|
2764
|
-
var import_os2 = __toESM(require("os"));
|
|
2765
2764
|
var import_path4 = require("path");
|
|
2766
2765
|
var import_build_utils4 = require("@vercel/build-utils");
|
|
2766
|
+
var import_python_analysis = require("@vercel/python-analysis");
|
|
2767
2767
|
|
|
2768
2768
|
// src/utils.ts
|
|
2769
2769
|
var import_fs2 = __toESM(require("fs"));
|
|
@@ -3061,7 +3061,8 @@ function createVenvEnv(venvPath, baseEnv = process.env) {
|
|
|
3061
3061
|
}
|
|
3062
3062
|
async function ensureVenv({
|
|
3063
3063
|
pythonPath,
|
|
3064
|
-
venvPath
|
|
3064
|
+
venvPath,
|
|
3065
|
+
uvPath
|
|
3065
3066
|
}) {
|
|
3066
3067
|
const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
|
|
3067
3068
|
try {
|
|
@@ -3071,7 +3072,11 @@ async function ensureVenv({
|
|
|
3071
3072
|
}
|
|
3072
3073
|
await import_fs2.default.promises.mkdir(venvPath, { recursive: true });
|
|
3073
3074
|
console.log(`Creating virtual environment at "${venvPath}"...`);
|
|
3074
|
-
|
|
3075
|
+
if (uvPath) {
|
|
3076
|
+
await (0, import_execa2.default)(uvPath, ["venv", venvPath]);
|
|
3077
|
+
} else {
|
|
3078
|
+
await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
|
|
3079
|
+
}
|
|
3075
3080
|
}
|
|
3076
3081
|
function getVenvPythonBin(venvPath) {
|
|
3077
3082
|
return (0, import_path3.join)(getVenvBinDir(venvPath), isWin2 ? "python.exe" : "python");
|
|
@@ -3379,7 +3384,6 @@ function isInstalled({ version: version2 }) {
|
|
|
3379
3384
|
}
|
|
3380
3385
|
|
|
3381
3386
|
// src/install.ts
|
|
3382
|
-
var isWin3 = process.platform === "win32";
|
|
3383
3387
|
var makeDependencyCheckCode = (dependency) => `
|
|
3384
3388
|
from importlib import util
|
|
3385
3389
|
dep = '${dependency}'.replace('-', '_')
|
|
@@ -3455,225 +3459,130 @@ function resolveVendorDir() {
|
|
|
3455
3459
|
const vendorDir = process.env.VERCEL_PYTHON_VENDOR_DIR || "_vendor";
|
|
3456
3460
|
return vendorDir;
|
|
3457
3461
|
}
|
|
3462
|
+
function toBuildError(error) {
|
|
3463
|
+
return new import_build_utils4.NowBuildError({
|
|
3464
|
+
code: error.code,
|
|
3465
|
+
message: error.message,
|
|
3466
|
+
link: error.link,
|
|
3467
|
+
action: error.action
|
|
3468
|
+
});
|
|
3469
|
+
}
|
|
3458
3470
|
async function detectInstallSource({
|
|
3459
3471
|
workPath,
|
|
3460
3472
|
entryDirectory,
|
|
3461
|
-
|
|
3473
|
+
repoRootPath
|
|
3462
3474
|
}) {
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
});
|
|
3475
|
-
const pipfileLockDir = findDir({
|
|
3476
|
-
file: "Pipfile.lock",
|
|
3477
|
-
entryDirectory,
|
|
3478
|
-
workPath,
|
|
3479
|
-
fsFiles
|
|
3480
|
-
});
|
|
3481
|
-
const pipfileDir = findDir({
|
|
3482
|
-
file: "Pipfile",
|
|
3483
|
-
entryDirectory,
|
|
3484
|
-
workPath,
|
|
3485
|
-
fsFiles
|
|
3486
|
-
});
|
|
3487
|
-
const requirementsDir = findDir({
|
|
3488
|
-
file: "requirements.txt",
|
|
3489
|
-
entryDirectory,
|
|
3490
|
-
workPath,
|
|
3491
|
-
fsFiles
|
|
3492
|
-
});
|
|
3493
|
-
let manifestPath = null;
|
|
3494
|
-
let manifestType = null;
|
|
3495
|
-
if (uvLockDir && pyprojectDir) {
|
|
3496
|
-
manifestType = "uv.lock";
|
|
3497
|
-
manifestPath = (0, import_path4.join)(uvLockDir, "uv.lock");
|
|
3498
|
-
} else if (pyprojectDir) {
|
|
3499
|
-
manifestType = "pyproject.toml";
|
|
3500
|
-
manifestPath = (0, import_path4.join)(pyprojectDir, "pyproject.toml");
|
|
3501
|
-
} else if (pipfileLockDir) {
|
|
3502
|
-
manifestType = "Pipfile.lock";
|
|
3503
|
-
manifestPath = (0, import_path4.join)(pipfileLockDir, "Pipfile.lock");
|
|
3504
|
-
} else if (pipfileDir) {
|
|
3505
|
-
manifestType = "Pipfile";
|
|
3506
|
-
manifestPath = (0, import_path4.join)(pipfileDir, "Pipfile");
|
|
3507
|
-
} else if (requirementsDir) {
|
|
3508
|
-
manifestType = "requirements.txt";
|
|
3509
|
-
manifestPath = (0, import_path4.join)(requirementsDir, "requirements.txt");
|
|
3510
|
-
}
|
|
3511
|
-
let manifestContent;
|
|
3512
|
-
if (manifestPath) {
|
|
3513
|
-
try {
|
|
3514
|
-
manifestContent = await import_fs3.default.promises.readFile(manifestPath, "utf8");
|
|
3515
|
-
} catch (err) {
|
|
3516
|
-
(0, import_build_utils4.debug)("Failed to read install manifest contents", err);
|
|
3475
|
+
const entrypointDir = (0, import_path4.join)(workPath, entryDirectory);
|
|
3476
|
+
const rootDir = repoRootPath ?? workPath;
|
|
3477
|
+
let pythonPackage;
|
|
3478
|
+
try {
|
|
3479
|
+
pythonPackage = await (0, import_python_analysis.discoverPythonPackage)({
|
|
3480
|
+
entrypointDir,
|
|
3481
|
+
rootDir
|
|
3482
|
+
});
|
|
3483
|
+
} catch (error) {
|
|
3484
|
+
if (error instanceof import_python_analysis.PythonAnalysisError) {
|
|
3485
|
+
throw toBuildError(error);
|
|
3517
3486
|
}
|
|
3487
|
+
throw error;
|
|
3518
3488
|
}
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
}) {
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
].join("\n") : "dependencies = []";
|
|
3534
|
-
const content = [
|
|
3535
|
-
"[project]",
|
|
3536
|
-
`name = "${projectName}"`,
|
|
3537
|
-
'version = "0.1.0"',
|
|
3538
|
-
`requires-python = "${requiresPython}"`,
|
|
3539
|
-
"classifiers = [",
|
|
3540
|
-
' "Private :: Do Not Upload",',
|
|
3541
|
-
"]",
|
|
3542
|
-
depsToml,
|
|
3543
|
-
""
|
|
3544
|
-
].join("\n");
|
|
3545
|
-
await import_fs3.default.promises.writeFile(pyprojectPath, content);
|
|
3546
|
-
}
|
|
3547
|
-
function findUvLockUpwards(startDir, repoRootPath) {
|
|
3548
|
-
const start = (0, import_path4.resolve)(startDir);
|
|
3549
|
-
const base = repoRootPath ? (0, import_path4.resolve)(repoRootPath) : void 0;
|
|
3550
|
-
for (const dir of (0, import_build_utils4.traverseUpDirectories)({ start, base })) {
|
|
3551
|
-
const lockPath = (0, import_path4.join)(dir, "uv.lock");
|
|
3552
|
-
const pyprojectPath = (0, import_path4.join)(dir, "pyproject.toml");
|
|
3553
|
-
if (import_fs3.default.existsSync(lockPath) && import_fs3.default.existsSync(pyprojectPath)) {
|
|
3554
|
-
return lockPath;
|
|
3555
|
-
}
|
|
3489
|
+
let manifestType = null;
|
|
3490
|
+
let manifestPath = null;
|
|
3491
|
+
const lockFile = pythonPackage.manifest?.lockFile ?? pythonPackage.workspaceLockFile;
|
|
3492
|
+
if (lockFile) {
|
|
3493
|
+
if (lockFile.kind === import_python_analysis.PythonLockFileKind.UvLock) {
|
|
3494
|
+
manifestType = "uv.lock";
|
|
3495
|
+
manifestPath = (0, import_path4.join)(rootDir, lockFile.path);
|
|
3496
|
+
} else if (lockFile.kind === import_python_analysis.PythonLockFileKind.PylockToml) {
|
|
3497
|
+
manifestType = "pylock.toml";
|
|
3498
|
+
manifestPath = (0, import_path4.join)(rootDir, lockFile.path);
|
|
3499
|
+
}
|
|
3500
|
+
} else if (pythonPackage.manifest) {
|
|
3501
|
+
manifestType = "pyproject.toml";
|
|
3502
|
+
manifestPath = (0, import_path4.join)(rootDir, pythonPackage.manifest.path);
|
|
3556
3503
|
}
|
|
3557
|
-
return
|
|
3504
|
+
return { manifestPath, manifestType, pythonPackage };
|
|
3558
3505
|
}
|
|
3559
3506
|
async function ensureUvProject({
|
|
3560
3507
|
workPath,
|
|
3561
3508
|
entryDirectory,
|
|
3562
|
-
fsFiles,
|
|
3563
3509
|
repoRootPath,
|
|
3564
|
-
pythonPath,
|
|
3565
|
-
pipPath,
|
|
3566
3510
|
pythonVersion,
|
|
3567
|
-
uv
|
|
3568
|
-
venvPath,
|
|
3569
|
-
meta
|
|
3511
|
+
uv
|
|
3570
3512
|
}) {
|
|
3571
|
-
const
|
|
3513
|
+
const rootDir = repoRootPath ?? workPath;
|
|
3572
3514
|
const installInfo = await detectInstallSource({
|
|
3573
3515
|
workPath,
|
|
3574
3516
|
entryDirectory,
|
|
3575
|
-
|
|
3517
|
+
repoRootPath
|
|
3576
3518
|
});
|
|
3577
|
-
const { manifestType,
|
|
3519
|
+
const { manifestType, pythonPackage } = installInfo;
|
|
3520
|
+
const manifest = pythonPackage?.manifest;
|
|
3578
3521
|
let projectDir;
|
|
3579
3522
|
let pyprojectPath;
|
|
3580
3523
|
let lockPath = null;
|
|
3581
|
-
if (manifestType === "uv.lock") {
|
|
3582
|
-
|
|
3583
|
-
|
|
3524
|
+
if (manifestType === "uv.lock" || manifestType === "pylock.toml") {
|
|
3525
|
+
const lockFile = pythonPackage?.manifest?.lockFile ?? pythonPackage?.workspaceLockFile;
|
|
3526
|
+
if (!lockFile) {
|
|
3527
|
+
throw new Error(
|
|
3528
|
+
`Expected lock file path to be resolved, but it was null`
|
|
3529
|
+
);
|
|
3584
3530
|
}
|
|
3585
|
-
|
|
3531
|
+
lockPath = (0, import_path4.join)(rootDir, lockFile.path);
|
|
3532
|
+
projectDir = (0, import_path4.dirname)(lockPath);
|
|
3586
3533
|
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3587
3534
|
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3588
3535
|
throw new Error(
|
|
3589
|
-
`Expected "pyproject.toml" next to "
|
|
3536
|
+
`Expected "pyproject.toml" next to "${lockFile.kind}" in "${projectDir}"`
|
|
3590
3537
|
);
|
|
3591
3538
|
}
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3539
|
+
console.log(`Installing required dependencies from ${lockFile.kind}...`);
|
|
3540
|
+
} else if (manifest) {
|
|
3541
|
+
projectDir = (0, import_path4.join)(rootDir, (0, import_path4.dirname)(manifest.path));
|
|
3542
|
+
pyprojectPath = (0, import_path4.join)(rootDir, manifest.path);
|
|
3543
|
+
const originKind = manifest.origin?.kind;
|
|
3544
|
+
if (originKind === import_python_analysis.PythonManifestConvertedKind.Pipfile) {
|
|
3545
|
+
console.log("Installing required dependencies from Pipfile...");
|
|
3546
|
+
} else if (originKind === import_python_analysis.PythonManifestConvertedKind.PipfileLock) {
|
|
3547
|
+
console.log("Installing required dependencies from Pipfile.lock...");
|
|
3548
|
+
} else if (originKind === import_python_analysis.PythonManifestConvertedKind.RequirementsTxt || originKind === import_python_analysis.PythonManifestConvertedKind.RequirementsIn) {
|
|
3549
|
+
console.log(
|
|
3550
|
+
`Installing required dependencies from ${manifest.origin?.path ?? "requirements.txt"}...`
|
|
3598
3551
|
);
|
|
3599
|
-
}
|
|
3600
|
-
projectDir = (0, import_path4.dirname)(manifestPath);
|
|
3601
|
-
pyprojectPath = manifestPath;
|
|
3602
|
-
console.log("Installing required dependencies from pyproject.toml...");
|
|
3603
|
-
const workspaceLock = findUvLockUpwards(projectDir, repoRootPath);
|
|
3604
|
-
if (workspaceLock) {
|
|
3605
|
-
lockPath = workspaceLock;
|
|
3606
3552
|
} else {
|
|
3607
|
-
|
|
3553
|
+
console.log("Installing required dependencies from pyproject.toml...");
|
|
3608
3554
|
}
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
);
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
console.log(`Installing required dependencies from ${manifestType}...`);
|
|
3617
|
-
const exportedReq = await exportRequirementsFromPipfile({
|
|
3618
|
-
pythonPath,
|
|
3619
|
-
pipPath,
|
|
3620
|
-
uvPath,
|
|
3621
|
-
projectDir,
|
|
3622
|
-
meta
|
|
3623
|
-
});
|
|
3624
|
-
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3625
|
-
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3626
|
-
await createPyprojectToml({
|
|
3627
|
-
projectName: "app",
|
|
3628
|
-
pyprojectPath,
|
|
3629
|
-
dependencies: [],
|
|
3630
|
-
pythonVersion
|
|
3631
|
-
});
|
|
3632
|
-
}
|
|
3633
|
-
await uv.addFromFile({
|
|
3634
|
-
venvPath,
|
|
3635
|
-
projectDir,
|
|
3636
|
-
requirementsPath: exportedReq
|
|
3637
|
-
});
|
|
3638
|
-
} else if (manifestType === "requirements.txt") {
|
|
3639
|
-
if (!manifestPath) {
|
|
3640
|
-
throw new Error(
|
|
3641
|
-
"Expected requirements.txt path to be resolved, but it was null"
|
|
3642
|
-
);
|
|
3555
|
+
if (manifest.origin) {
|
|
3556
|
+
if (manifest.data.project && !manifest.data.project["requires-python"]) {
|
|
3557
|
+
manifest.data.project["requires-python"] = `~=${pythonVersion}.0`;
|
|
3558
|
+
}
|
|
3559
|
+
const content = (0, import_python_analysis.stringifyManifest)(manifest.data);
|
|
3560
|
+
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3561
|
+
await import_fs3.default.promises.writeFile(pyprojectPath, content);
|
|
3643
3562
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
if (!import_fs3.default.existsSync(pyprojectPath)) {
|
|
3650
|
-
await createPyprojectToml({
|
|
3651
|
-
projectName: "app",
|
|
3652
|
-
pyprojectPath,
|
|
3653
|
-
dependencies: [],
|
|
3654
|
-
pythonVersion
|
|
3655
|
-
});
|
|
3563
|
+
const workspaceLockFile = pythonPackage?.workspaceLockFile;
|
|
3564
|
+
if (workspaceLockFile) {
|
|
3565
|
+
lockPath = (0, import_path4.join)(rootDir, workspaceLockFile.path);
|
|
3566
|
+
} else {
|
|
3567
|
+
await uv.lock(projectDir);
|
|
3656
3568
|
}
|
|
3657
|
-
await uv.addFromFile({
|
|
3658
|
-
venvPath,
|
|
3659
|
-
projectDir,
|
|
3660
|
-
requirementsPath: manifestPath
|
|
3661
|
-
});
|
|
3662
3569
|
} else {
|
|
3663
3570
|
projectDir = workPath;
|
|
3664
3571
|
pyprojectPath = (0, import_path4.join)(projectDir, "pyproject.toml");
|
|
3665
3572
|
console.log(
|
|
3666
3573
|
"No Python manifest found; creating an empty pyproject.toml and uv.lock..."
|
|
3667
3574
|
);
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3575
|
+
const requiresPython = `~=${pythonVersion}.0`;
|
|
3576
|
+
const minimalManifest = (0, import_python_analysis.createMinimalManifest)({
|
|
3577
|
+
name: "app",
|
|
3578
|
+
requiresPython,
|
|
3579
|
+
dependencies: []
|
|
3673
3580
|
});
|
|
3581
|
+
const content = (0, import_python_analysis.stringifyManifest)(minimalManifest);
|
|
3582
|
+
await import_fs3.default.promises.writeFile(pyprojectPath, content);
|
|
3674
3583
|
await uv.lock(projectDir);
|
|
3675
3584
|
}
|
|
3676
|
-
const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath :
|
|
3585
|
+
const resolvedLockPath = lockPath && import_fs3.default.existsSync(lockPath) ? lockPath : (0, import_path4.join)(projectDir, "uv.lock");
|
|
3677
3586
|
return { projectDir, pyprojectPath, lockPath: resolvedLockPath };
|
|
3678
3587
|
}
|
|
3679
3588
|
async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
|
|
@@ -3767,46 +3676,6 @@ async function installRequirementsFile({
|
|
|
3767
3676
|
targetDir
|
|
3768
3677
|
);
|
|
3769
3678
|
}
|
|
3770
|
-
async function exportRequirementsFromPipfile({
|
|
3771
|
-
pythonPath,
|
|
3772
|
-
pipPath,
|
|
3773
|
-
uvPath,
|
|
3774
|
-
projectDir,
|
|
3775
|
-
meta
|
|
3776
|
-
}) {
|
|
3777
|
-
const tempDir = await import_fs3.default.promises.mkdtemp(
|
|
3778
|
-
(0, import_path4.join)(import_os2.default.tmpdir(), "vercel-pipenv-")
|
|
3779
|
-
);
|
|
3780
|
-
await installRequirement({
|
|
3781
|
-
pythonPath,
|
|
3782
|
-
pipPath,
|
|
3783
|
-
dependency: "pipfile-requirements",
|
|
3784
|
-
version: "0.3.0",
|
|
3785
|
-
workPath: tempDir,
|
|
3786
|
-
meta,
|
|
3787
|
-
args: ["--no-warn-script-location"],
|
|
3788
|
-
uvPath
|
|
3789
|
-
});
|
|
3790
|
-
const tempVendorDir = (0, import_path4.join)(tempDir, resolveVendorDir());
|
|
3791
|
-
const convertCmd = isWin3 ? (0, import_path4.join)(tempVendorDir, "Scripts", "pipfile2req.exe") : (0, import_path4.join)(tempVendorDir, "bin", "pipfile2req");
|
|
3792
|
-
(0, import_build_utils4.debug)(`Running "${convertCmd}" in ${projectDir}...`);
|
|
3793
|
-
let stdout;
|
|
3794
|
-
try {
|
|
3795
|
-
const { stdout: out } = await (0, import_execa3.default)(convertCmd, [], {
|
|
3796
|
-
cwd: projectDir,
|
|
3797
|
-
env: { ...process.env, PYTHONPATH: tempVendorDir }
|
|
3798
|
-
});
|
|
3799
|
-
stdout = out;
|
|
3800
|
-
} catch (err) {
|
|
3801
|
-
throw new Error(
|
|
3802
|
-
`Failed to run "${convertCmd}": ${err instanceof Error ? err.message : String(err)}`
|
|
3803
|
-
);
|
|
3804
|
-
}
|
|
3805
|
-
const outPath = (0, import_path4.join)(tempDir, "requirements.pipenv.txt");
|
|
3806
|
-
await import_fs3.default.promises.writeFile(outPath, stdout);
|
|
3807
|
-
(0, import_build_utils4.debug)(`Exported pipfile requirements to ${outPath}`);
|
|
3808
|
-
return outPath;
|
|
3809
|
-
}
|
|
3810
3679
|
async function mirrorSitePackagesIntoVendor({
|
|
3811
3680
|
venvPath,
|
|
3812
3681
|
vendorDirName
|
|
@@ -4044,6 +3913,21 @@ function createDevWsgiShim(workPath, modulePath) {
|
|
|
4044
3913
|
return null;
|
|
4045
3914
|
}
|
|
4046
3915
|
}
|
|
3916
|
+
async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath) {
|
|
3917
|
+
const { pythonCmd, venvRoot } = useVirtualEnv(workPath, env, systemPython);
|
|
3918
|
+
if (venvRoot) {
|
|
3919
|
+
(0, import_build_utils7.debug)(`Using existing virtualenv at ${venvRoot} for multi-service dev`);
|
|
3920
|
+
return { command: pythonCmd, args: [] };
|
|
3921
|
+
}
|
|
3922
|
+
const venvPath = (0, import_path6.join)(workPath, ".venv");
|
|
3923
|
+
await ensureVenv({ pythonPath: systemPython, venvPath, uvPath });
|
|
3924
|
+
(0, import_build_utils7.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
|
|
3925
|
+
const pythonBin = getVenvPythonBin(venvPath);
|
|
3926
|
+
const binDir = getVenvBinDir(venvPath);
|
|
3927
|
+
env.VIRTUAL_ENV = venvPath;
|
|
3928
|
+
env.PATH = `${binDir}${import_path6.delimiter}${env.PATH || ""}`;
|
|
3929
|
+
return { command: pythonBin, args: [] };
|
|
3930
|
+
}
|
|
4047
3931
|
var startDevServer = async (opts) => {
|
|
4048
3932
|
const {
|
|
4049
3933
|
entrypoint: rawEntrypoint,
|
|
@@ -4104,44 +3988,70 @@ var startDevServer = async (opts) => {
|
|
|
4104
3988
|
let resolveChildReady;
|
|
4105
3989
|
let rejectChildReady;
|
|
4106
3990
|
const childReady = new Promise(
|
|
4107
|
-
(
|
|
4108
|
-
resolveChildReady =
|
|
3991
|
+
(resolve, reject) => {
|
|
3992
|
+
resolveChildReady = resolve;
|
|
4109
3993
|
rejectChildReady = reject;
|
|
4110
3994
|
}
|
|
4111
3995
|
);
|
|
4112
3996
|
PENDING_STARTS.set(serverKey, childReady);
|
|
4113
3997
|
try {
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
3998
|
+
const { pythonPath: systemPython } = getDefaultPythonVersion(meta);
|
|
3999
|
+
const uvPath = await findUvBinary(systemPython);
|
|
4000
|
+
const venv = isInVirtualEnv();
|
|
4001
|
+
const serviceCount = meta.serviceCount ?? 0;
|
|
4002
|
+
const pythonServiceCount = meta.pythonServiceCount ?? 1;
|
|
4003
|
+
if (venv && pythonServiceCount > 1) {
|
|
4004
|
+
const yellow = "\x1B[33m";
|
|
4005
|
+
const white = "\x1B[1m";
|
|
4006
|
+
const reset = "\x1B[0m";
|
|
4007
|
+
throw new import_build_utils7.NowBuildError({
|
|
4008
|
+
code: "PYTHON_EXTERNAL_VENV_DETECTED",
|
|
4009
|
+
message: `Detected activated venv at ${yellow}${venv}${reset}, ${white}vercel dev${reset} manages virtual environments automatically.
|
|
4010
|
+
Run ${white}deactivate${reset} and try again.`
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
let spawnCommand = systemPython;
|
|
4014
|
+
let spawnArgsPrefix = [];
|
|
4015
|
+
if (serviceCount > 0) {
|
|
4016
|
+
const runner = await getMultiServicePythonRunner(
|
|
4017
|
+
workPath,
|
|
4018
|
+
env,
|
|
4019
|
+
systemPython,
|
|
4020
|
+
uvPath
|
|
4021
|
+
);
|
|
4022
|
+
spawnCommand = runner.command;
|
|
4023
|
+
spawnArgsPrefix = runner.args;
|
|
4024
|
+
(0, import_build_utils7.debug)(
|
|
4025
|
+
`Multi-service Python runner: ${spawnCommand} ${spawnArgsPrefix.join(" ")}`
|
|
4026
|
+
);
|
|
4027
|
+
} else if (venv) {
|
|
4028
|
+
(0, import_build_utils7.debug)(`Running in virtualenv at ${venv}`);
|
|
4029
|
+
} else {
|
|
4030
|
+
const { pythonCmd: venvPythonCmd, venvRoot } = useVirtualEnv(
|
|
4031
|
+
workPath,
|
|
4032
|
+
env,
|
|
4033
|
+
systemPython
|
|
4034
|
+
);
|
|
4035
|
+
spawnCommand = venvPythonCmd;
|
|
4036
|
+
if (venvRoot) {
|
|
4037
|
+
(0, import_build_utils7.debug)(`Using virtualenv at ${venvRoot}`);
|
|
4121
4038
|
} else {
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
(0, import_build_utils7.debug)(`Using virtualenv at ${venvRoot}`);
|
|
4130
|
-
} else {
|
|
4131
|
-
(0, import_build_utils7.debug)("No virtualenv found");
|
|
4132
|
-
try {
|
|
4133
|
-
const yellow = "\x1B[33m";
|
|
4134
|
-
const reset = "\x1B[0m";
|
|
4135
|
-
const venvCmd = process.platform === "win32" ? "python -m venv .venv && .venv\\Scripts\\activate" : "python -m venv .venv && source .venv/bin/activate";
|
|
4136
|
-
process.stderr.write(
|
|
4137
|
-
`${yellow}Warning: no virtual environment detected in ${workPath}. Using system Python: ${pythonCmd}.${reset}
|
|
4039
|
+
(0, import_build_utils7.debug)("No virtualenv found");
|
|
4040
|
+
try {
|
|
4041
|
+
const yellow = "\x1B[33m";
|
|
4042
|
+
const reset = "\x1B[0m";
|
|
4043
|
+
const venvCmd = process.platform === "win32" ? "python -m venv .venv && .venv\\Scripts\\activate" : "python -m venv .venv && source .venv/bin/activate";
|
|
4044
|
+
process.stderr.write(
|
|
4045
|
+
`${yellow}Warning: no virtual environment detected in ${workPath}. Using system Python: ${systemPython}.${reset}
|
|
4138
4046
|
If you are using a virtual environment, activate it before running "vercel dev", or create one: ${venvCmd}
|
|
4139
4047
|
`
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
}
|
|
4048
|
+
);
|
|
4049
|
+
} catch (_) {
|
|
4143
4050
|
}
|
|
4144
4051
|
}
|
|
4052
|
+
}
|
|
4053
|
+
await new Promise((resolve, reject) => {
|
|
4054
|
+
let resolved = false;
|
|
4145
4055
|
if (framework !== "flask") {
|
|
4146
4056
|
const devShimModule = createDevAsgiShim(workPath, modulePath);
|
|
4147
4057
|
if (devShimModule) {
|
|
@@ -4150,11 +4060,12 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4150
4060
|
env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
|
|
4151
4061
|
}
|
|
4152
4062
|
const moduleToRun = devShimModule || modulePath;
|
|
4153
|
-
const
|
|
4063
|
+
const pythonArgs = ["-u", "-m", moduleToRun];
|
|
4064
|
+
const argv = [...spawnArgsPrefix, ...pythonArgs];
|
|
4154
4065
|
(0, import_build_utils7.debug)(
|
|
4155
|
-
`Starting ASGI dev server (${framework}): ${
|
|
4066
|
+
`Starting ASGI dev server (${framework}): ${spawnCommand} ${argv.join(" ")}`
|
|
4156
4067
|
);
|
|
4157
|
-
const child = (0, import_child_process2.spawn)(
|
|
4068
|
+
const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
|
|
4158
4069
|
cwd: workPath,
|
|
4159
4070
|
env,
|
|
4160
4071
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -4187,7 +4098,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4187
4098
|
child.stderr?.removeListener("data", onDetect);
|
|
4188
4099
|
const port2 = Number(portMatch[1]);
|
|
4189
4100
|
resolveChildReady({ port: port2, pid: child.pid });
|
|
4190
|
-
|
|
4101
|
+
resolve();
|
|
4191
4102
|
}
|
|
4192
4103
|
}
|
|
4193
4104
|
};
|
|
@@ -4216,9 +4127,10 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4216
4127
|
env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
|
|
4217
4128
|
}
|
|
4218
4129
|
const moduleToRun = devShimModule || modulePath;
|
|
4219
|
-
const
|
|
4220
|
-
|
|
4221
|
-
|
|
4130
|
+
const pythonArgs = ["-u", "-m", moduleToRun];
|
|
4131
|
+
const argv = [...spawnArgsPrefix, ...pythonArgs];
|
|
4132
|
+
(0, import_build_utils7.debug)(`Starting Flask dev server: ${spawnCommand} ${argv.join(" ")}`);
|
|
4133
|
+
const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
|
|
4222
4134
|
cwd: workPath,
|
|
4223
4135
|
env,
|
|
4224
4136
|
stdio: ["inherit", "pipe", "pipe"]
|
|
@@ -4250,7 +4162,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4250
4162
|
child.stderr?.removeListener("data", onDetect);
|
|
4251
4163
|
const port2 = Number(portMatch[1]);
|
|
4252
4164
|
resolveChildReady({ port: port2, pid: child.pid });
|
|
4253
|
-
|
|
4165
|
+
resolve();
|
|
4254
4166
|
}
|
|
4255
4167
|
}
|
|
4256
4168
|
};
|
|
@@ -4522,14 +4434,9 @@ var build = async ({
|
|
|
4522
4434
|
const { projectDir } = await ensureUvProject({
|
|
4523
4435
|
workPath,
|
|
4524
4436
|
entryDirectory,
|
|
4525
|
-
fsFiles,
|
|
4526
4437
|
repoRootPath,
|
|
4527
|
-
pythonPath: pythonVersion.pythonPath,
|
|
4528
|
-
pipPath: pythonVersion.pipPath,
|
|
4529
4438
|
pythonVersion: pythonVersion.version,
|
|
4530
|
-
uv
|
|
4531
|
-
venvPath,
|
|
4532
|
-
meta
|
|
4439
|
+
uv
|
|
4533
4440
|
});
|
|
4534
4441
|
await uv.sync({
|
|
4535
4442
|
venvPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"url": "https://github.com/vercel/vercel.git",
|
|
16
16
|
"directory": "packages/python"
|
|
17
17
|
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@vercel/python-analysis": "0.4.0"
|
|
20
|
+
},
|
|
18
21
|
"devDependencies": {
|
|
19
22
|
"@renovatebot/pep440": "4.2.1",
|
|
20
23
|
"@types/execa": "^0.9.0",
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"smol-toml": "1.5.2",
|
|
32
35
|
"vitest": "2.1.4",
|
|
33
36
|
"which": "3.0.0",
|
|
34
|
-
"@vercel/build-utils": "13.3.
|
|
37
|
+
"@vercel/build-utils": "13.3.5",
|
|
35
38
|
"@vercel/error-utils": "2.0.3"
|
|
36
39
|
},
|
|
37
40
|
"scripts": {
|
package/vc_init_dev_asgi.py
CHANGED
|
@@ -75,13 +75,23 @@ async def app(scope, receive, send):
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
if __name__ == '__main__':
|
|
78
|
-
# Development runner for ASGI: prefer uvicorn, then hypercorn.
|
|
78
|
+
# Development runner for ASGI: prefer fastapi dev, then uvicorn, then hypercorn.
|
|
79
79
|
# Bind to localhost on an ephemeral port and emit a recognizable log line
|
|
80
80
|
# so the caller can detect the bound port.
|
|
81
81
|
host = '127.0.0.1'
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
from fastapi_cli.cli import dev
|
|
85
|
+
except ImportError:
|
|
86
|
+
dev = None
|
|
87
|
+
|
|
88
|
+
if dev is not None:
|
|
89
|
+
dev(entrypoint='vc_init_dev_asgi:app', host=host, port=0, reload=True)
|
|
90
|
+
sys.exit(0)
|
|
91
|
+
|
|
82
92
|
try:
|
|
83
93
|
import uvicorn
|
|
84
|
-
uvicorn.run('vc_init_dev_asgi:app', host=host, port=0, use_colors=True)
|
|
94
|
+
uvicorn.run('vc_init_dev_asgi:app', host=host, port=0, use_colors=True, reload=True)
|
|
85
95
|
except Exception:
|
|
86
96
|
try:
|
|
87
97
|
import asyncio
|
package/vc_init_dev_wsgi.py
CHANGED
|
@@ -112,7 +112,7 @@ if __name__ == "__main__":
|
|
|
112
112
|
host = "127.0.0.1"
|
|
113
113
|
try:
|
|
114
114
|
from werkzeug.serving import run_simple
|
|
115
|
-
run_simple(host, 0, app, use_reloader=
|
|
115
|
+
run_simple(host, 0, app, use_reloader=True)
|
|
116
116
|
except Exception:
|
|
117
117
|
import sys
|
|
118
118
|
print(_color("Werkzeug not installed; falling back to wsgiref (no reloader).", _YELLOW), file=sys.stderr)
|