@vercel/python 6.21.0 → 6.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +91 -70
- package/package.json +4 -4
- package/templates/vc_init_dev.py +7 -588
package/dist/index.js
CHANGED
|
@@ -2862,7 +2862,6 @@ __export(src_exports, {
|
|
|
2862
2862
|
downloadFilesInWorkPath: () => downloadFilesInWorkPath,
|
|
2863
2863
|
installRequirement: () => installRequirement,
|
|
2864
2864
|
installRequirementsFile: () => installRequirementsFile,
|
|
2865
|
-
prepareCache: () => prepareCache,
|
|
2866
2865
|
runFrameworkHook: () => runFrameworkHook,
|
|
2867
2866
|
shouldServe: () => shouldServe,
|
|
2868
2867
|
startDevServer: () => startDevServer,
|
|
@@ -2873,7 +2872,7 @@ var import_fs10 = __toESM(require("fs"));
|
|
|
2873
2872
|
var import_path12 = require("path");
|
|
2874
2873
|
|
|
2875
2874
|
// src/package-versions.ts
|
|
2876
|
-
var VERCEL_RUNTIME_VERSION = "0.
|
|
2875
|
+
var VERCEL_RUNTIME_VERSION = "0.7.0";
|
|
2877
2876
|
var VERCEL_WORKERS_VERSION = "0.0.12";
|
|
2878
2877
|
|
|
2879
2878
|
// src/index.ts
|
|
@@ -2904,19 +2903,14 @@ var import_build_utils = require("@vercel/build-utils");
|
|
|
2904
2903
|
var UV_VERSION = "0.9.22";
|
|
2905
2904
|
var UV_PYTHON_PATH_PREFIX = "/uv/python/";
|
|
2906
2905
|
var UV_PYTHON_DOWNLOADS_MODE = "automatic";
|
|
2907
|
-
var UV_CACHE_DIR_SUBPATH = [".vercel", "python", "cache", "uv"];
|
|
2908
2906
|
var isWin = process.platform === "win32";
|
|
2909
2907
|
var uvExec = isWin ? "uv.exe" : "uv";
|
|
2910
2908
|
function findUvInPath() {
|
|
2911
2909
|
return import_which.default.sync("uv", { nothrow: true });
|
|
2912
2910
|
}
|
|
2913
|
-
function getUvCacheDir(workPath) {
|
|
2914
|
-
return (0, import_path.join)(workPath, ...UV_CACHE_DIR_SUBPATH);
|
|
2915
|
-
}
|
|
2916
2911
|
var UvRunner = class {
|
|
2917
|
-
constructor(uvPath
|
|
2912
|
+
constructor(uvPath) {
|
|
2918
2913
|
this.uvPath = uvPath;
|
|
2919
|
-
this.uvCacheDir = uvCacheDir;
|
|
2920
2914
|
}
|
|
2921
2915
|
getPath() {
|
|
2922
2916
|
return this.uvPath;
|
|
@@ -2993,7 +2987,7 @@ var UvRunner = class {
|
|
|
2993
2987
|
try {
|
|
2994
2988
|
await (0, import_execa.default)(this.uvPath, args, {
|
|
2995
2989
|
cwd: projectDir,
|
|
2996
|
-
env: getProtectedUvEnv(process.env
|
|
2990
|
+
env: getProtectedUvEnv(process.env)
|
|
2997
2991
|
});
|
|
2998
2992
|
} catch (err) {
|
|
2999
2993
|
const error = new Error(
|
|
@@ -3058,7 +3052,7 @@ var UvRunner = class {
|
|
|
3058
3052
|
const binDir = isWin ? (0, import_path.join)(venvPath, "Scripts") : (0, import_path.join)(venvPath, "bin");
|
|
3059
3053
|
const existingPath = process.env.PATH || "";
|
|
3060
3054
|
return {
|
|
3061
|
-
...getProtectedUvEnv(process.env
|
|
3055
|
+
...getProtectedUvEnv(process.env),
|
|
3062
3056
|
VIRTUAL_ENV: venvPath,
|
|
3063
3057
|
PATH: existingPath ? `${binDir}${import_path2.delimiter}${existingPath}` : binDir
|
|
3064
3058
|
};
|
|
@@ -3165,15 +3159,11 @@ async function getUvBinaryOrInstall(pythonPath) {
|
|
|
3165
3159
|
function filterUnsafeUvPipArgs(args) {
|
|
3166
3160
|
return args.filter((arg) => arg !== "--no-warn-script-location");
|
|
3167
3161
|
}
|
|
3168
|
-
function getProtectedUvEnv(baseEnv = process.env
|
|
3169
|
-
|
|
3162
|
+
function getProtectedUvEnv(baseEnv = process.env) {
|
|
3163
|
+
return {
|
|
3170
3164
|
...baseEnv,
|
|
3171
3165
|
UV_PYTHON_DOWNLOADS: UV_PYTHON_DOWNLOADS_MODE
|
|
3172
3166
|
};
|
|
3173
|
-
if (uvCacheDir) {
|
|
3174
|
-
env.UV_CACHE_DIR = uvCacheDir;
|
|
3175
|
-
}
|
|
3176
|
-
return env;
|
|
3177
3167
|
}
|
|
3178
3168
|
var UV_BUNDLE_DIR = "_uv";
|
|
3179
3169
|
async function getUvBinaryForBundling(pythonPath) {
|
|
@@ -3212,9 +3202,9 @@ function useVirtualEnv(workPath, env, systemPython) {
|
|
|
3212
3202
|
}
|
|
3213
3203
|
return { pythonCmd };
|
|
3214
3204
|
}
|
|
3215
|
-
function createVenvEnv(venvPath, baseEnv = process.env
|
|
3205
|
+
function createVenvEnv(venvPath, baseEnv = process.env) {
|
|
3216
3206
|
const env = {
|
|
3217
|
-
...getProtectedUvEnv(baseEnv
|
|
3207
|
+
...getProtectedUvEnv(baseEnv),
|
|
3218
3208
|
VIRTUAL_ENV: venvPath
|
|
3219
3209
|
};
|
|
3220
3210
|
const binDir = getVenvBinDir(venvPath);
|
|
@@ -3226,7 +3216,6 @@ async function ensureVenv({
|
|
|
3226
3216
|
pythonPath,
|
|
3227
3217
|
venvPath,
|
|
3228
3218
|
uvPath,
|
|
3229
|
-
uvCacheDir,
|
|
3230
3219
|
quiet
|
|
3231
3220
|
}) {
|
|
3232
3221
|
const marker = (0, import_path3.join)(venvPath, "pyvenv.cfg");
|
|
@@ -3240,9 +3229,7 @@ async function ensureVenv({
|
|
|
3240
3229
|
console.log(`Creating virtual environment at "${venvPath}"...`);
|
|
3241
3230
|
}
|
|
3242
3231
|
if (uvPath) {
|
|
3243
|
-
await (0, import_execa2.default)(uvPath, ["venv", venvPath]
|
|
3244
|
-
env: getProtectedUvEnv(process.env, uvCacheDir)
|
|
3245
|
-
});
|
|
3232
|
+
await (0, import_execa2.default)(uvPath, ["venv", venvPath]);
|
|
3246
3233
|
} else {
|
|
3247
3234
|
await (0, import_execa2.default)(pythonPath, ["-m", "venv", venvPath]);
|
|
3248
3235
|
}
|
|
@@ -3749,7 +3736,7 @@ async function pipInstall(pipPath, uvPath, workPath, args, targetDir) {
|
|
|
3749
3736
|
try {
|
|
3750
3737
|
await (0, import_execa3.default)(uvPath, uvArgs, {
|
|
3751
3738
|
cwd: workPath,
|
|
3752
|
-
env: getProtectedUvEnv(
|
|
3739
|
+
env: getProtectedUvEnv()
|
|
3753
3740
|
});
|
|
3754
3741
|
return;
|
|
3755
3742
|
} catch (err) {
|
|
@@ -4568,6 +4555,77 @@ async function runSync({
|
|
|
4568
4555
|
});
|
|
4569
4556
|
});
|
|
4570
4557
|
}
|
|
4558
|
+
async function installVercelRuntime({
|
|
4559
|
+
workPath,
|
|
4560
|
+
uvPath,
|
|
4561
|
+
pythonBin,
|
|
4562
|
+
env,
|
|
4563
|
+
onStdout,
|
|
4564
|
+
onStderr
|
|
4565
|
+
}) {
|
|
4566
|
+
const targetDir = (0, import_path8.join)(workPath, ".vercel", "python");
|
|
4567
|
+
(0, import_fs6.mkdirSync)(targetDir, { recursive: true });
|
|
4568
|
+
const localRuntimeDir = (0, import_path8.join)(
|
|
4569
|
+
__dirname,
|
|
4570
|
+
"..",
|
|
4571
|
+
"..",
|
|
4572
|
+
"..",
|
|
4573
|
+
"python",
|
|
4574
|
+
"vercel-runtime"
|
|
4575
|
+
);
|
|
4576
|
+
const isLocalDev = (0, import_fs6.existsSync)((0, import_path8.join)(localRuntimeDir, "pyproject.toml"));
|
|
4577
|
+
const runtimeDep = env.VERCEL_RUNTIME_PYTHON || (isLocalDev ? localRuntimeDir : `vercel-runtime==${VERCEL_RUNTIME_VERSION}`);
|
|
4578
|
+
if (!isLocalDev && !env.VERCEL_RUNTIME_PYTHON) {
|
|
4579
|
+
const distInfo = (0, import_path8.join)(
|
|
4580
|
+
targetDir,
|
|
4581
|
+
`vercel_runtime-${VERCEL_RUNTIME_VERSION}.dist-info`
|
|
4582
|
+
);
|
|
4583
|
+
if ((0, import_fs6.existsSync)(distInfo)) {
|
|
4584
|
+
(0, import_build_utils8.debug)(
|
|
4585
|
+
`vercel-runtime ${VERCEL_RUNTIME_VERSION} already installed, skipping`
|
|
4586
|
+
);
|
|
4587
|
+
return;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
(0, import_build_utils8.debug)(
|
|
4591
|
+
`Installing vercel-runtime into ${targetDir} (type: ${isLocalDev ? "local" : "pypi"}, source: ${runtimeDep})`
|
|
4592
|
+
);
|
|
4593
|
+
const pip = uvPath ? { cmd: uvPath, prefix: ["pip", "install"] } : { cmd: pythonBin, prefix: ["-m", "pip", "install"] };
|
|
4594
|
+
const spawnArgs = [...pip.prefix, "--target", targetDir, runtimeDep];
|
|
4595
|
+
await new Promise((resolve2, reject) => {
|
|
4596
|
+
const child = (0, import_child_process2.spawn)(pip.cmd, spawnArgs, {
|
|
4597
|
+
cwd: workPath,
|
|
4598
|
+
env: getProtectedUvEnv(env),
|
|
4599
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
4600
|
+
});
|
|
4601
|
+
child.stdout?.on("data", (data) => {
|
|
4602
|
+
if (onStdout) {
|
|
4603
|
+
onStdout(data);
|
|
4604
|
+
} else {
|
|
4605
|
+
(0, import_build_utils8.debug)(data.toString());
|
|
4606
|
+
}
|
|
4607
|
+
});
|
|
4608
|
+
child.stderr?.on("data", (data) => {
|
|
4609
|
+
if (onStderr) {
|
|
4610
|
+
onStderr(data);
|
|
4611
|
+
} else {
|
|
4612
|
+
(0, import_build_utils8.debug)(data.toString());
|
|
4613
|
+
}
|
|
4614
|
+
});
|
|
4615
|
+
child.on("error", reject);
|
|
4616
|
+
child.on("exit", (code, signal) => {
|
|
4617
|
+
if (code === 0) {
|
|
4618
|
+
resolve2();
|
|
4619
|
+
} else {
|
|
4620
|
+
reject(
|
|
4621
|
+
new Error(
|
|
4622
|
+
`Installing vercel-runtime failed with code ${code}, signal ${signal}`
|
|
4623
|
+
)
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
});
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4571
4629
|
var PERSISTENT_SERVERS = /* @__PURE__ */ new Map();
|
|
4572
4630
|
var PENDING_STARTS = /* @__PURE__ */ new Map();
|
|
4573
4631
|
var restoreWarnings = null;
|
|
@@ -4645,12 +4703,7 @@ async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath)
|
|
|
4645
4703
|
return { command: pythonCmd, args: [] };
|
|
4646
4704
|
}
|
|
4647
4705
|
const venvPath = (0, import_path8.join)(workPath, ".venv");
|
|
4648
|
-
await ensureVenv({
|
|
4649
|
-
pythonPath: systemPython,
|
|
4650
|
-
venvPath,
|
|
4651
|
-
uvPath,
|
|
4652
|
-
quiet: true
|
|
4653
|
-
});
|
|
4706
|
+
await ensureVenv({ pythonPath: systemPython, venvPath, uvPath, quiet: true });
|
|
4654
4707
|
(0, import_build_utils8.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
|
|
4655
4708
|
const pythonBin = getVenvPythonBin(venvPath);
|
|
4656
4709
|
const binDir = getVenvBinDir(venvPath);
|
|
@@ -4809,6 +4862,12 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
4809
4862
|
onStderr
|
|
4810
4863
|
});
|
|
4811
4864
|
}
|
|
4865
|
+
await installVercelRuntime({
|
|
4866
|
+
workPath,
|
|
4867
|
+
uvPath,
|
|
4868
|
+
pythonBin: spawnCommand,
|
|
4869
|
+
env
|
|
4870
|
+
});
|
|
4812
4871
|
const port = typeof meta.port === "number" ? meta.port : await (0, import_get_port.default)();
|
|
4813
4872
|
env.PORT = `${port}`;
|
|
4814
4873
|
const devShim = createDevShim(workPath, entry, modulePath);
|
|
@@ -5507,12 +5566,10 @@ var build = async ({
|
|
|
5507
5566
|
}
|
|
5508
5567
|
fsFiles = await (0, import_build_utils13.glob)("**", workPath);
|
|
5509
5568
|
const venvPath = (0, import_path12.join)(workPath, ".vercel", "python", ".venv");
|
|
5510
|
-
const uvCacheDir = getUvCacheDir(workPath);
|
|
5511
5569
|
await builderSpan.child("vc.builder.python.venv").trace(async () => {
|
|
5512
5570
|
await ensureVenv({
|
|
5513
5571
|
pythonPath: pythonVersion.pythonPath,
|
|
5514
|
-
venvPath
|
|
5515
|
-
uvCacheDir
|
|
5572
|
+
venvPath
|
|
5516
5573
|
});
|
|
5517
5574
|
});
|
|
5518
5575
|
if ((0, import_build_utils13.isPythonFramework)(framework)) {
|
|
@@ -5541,14 +5598,14 @@ var build = async ({
|
|
|
5541
5598
|
}
|
|
5542
5599
|
}
|
|
5543
5600
|
const baseEnv = spawnEnv || process.env;
|
|
5544
|
-
const pythonEnv = createVenvEnv(venvPath, baseEnv
|
|
5601
|
+
const pythonEnv = createVenvEnv(venvPath, baseEnv);
|
|
5545
5602
|
pythonEnv.VERCEL_PYTHON_VENV_PATH = venvPath;
|
|
5546
5603
|
let assumeDepsInstalled = false;
|
|
5547
5604
|
let uv;
|
|
5548
5605
|
try {
|
|
5549
5606
|
const uvPath = await getUvBinaryOrInstall(pythonVersion.pythonPath);
|
|
5550
5607
|
console.log(`Using uv at "${uvPath}"`);
|
|
5551
|
-
uv = new UvRunner(uvPath
|
|
5608
|
+
uv = new UvRunner(uvPath);
|
|
5552
5609
|
} catch (err) {
|
|
5553
5610
|
console.log("Failed to install or locate uv");
|
|
5554
5611
|
throw new Error(
|
|
@@ -5805,41 +5862,6 @@ from vercel_runtime.vc_init import vc_handler
|
|
|
5805
5862
|
});
|
|
5806
5863
|
return { output };
|
|
5807
5864
|
};
|
|
5808
|
-
async function readBuildOutputV3Config(workPath) {
|
|
5809
|
-
try {
|
|
5810
|
-
const configPath = (0, import_path12.join)(workPath, ".vercel", "output", "config.json");
|
|
5811
|
-
return JSON.parse(await import_fs10.default.promises.readFile(configPath, "utf8"));
|
|
5812
|
-
} catch (err) {
|
|
5813
|
-
if (err.code !== "ENOENT") {
|
|
5814
|
-
throw err;
|
|
5815
|
-
}
|
|
5816
|
-
}
|
|
5817
|
-
return void 0;
|
|
5818
|
-
}
|
|
5819
|
-
var prepareCache = async ({
|
|
5820
|
-
repoRootPath,
|
|
5821
|
-
workPath
|
|
5822
|
-
}) => {
|
|
5823
|
-
const cacheFiles = {};
|
|
5824
|
-
const root = repoRootPath || workPath;
|
|
5825
|
-
const ignore = ["**/*.pyc", "**/__pycache__/**"];
|
|
5826
|
-
const configV3 = await readBuildOutputV3Config(workPath);
|
|
5827
|
-
if (configV3?.cache && Array.isArray(configV3.cache)) {
|
|
5828
|
-
for (const cacheGlob of configV3.cache) {
|
|
5829
|
-
Object.assign(cacheFiles, await (0, import_build_utils13.glob)(cacheGlob, workPath));
|
|
5830
|
-
}
|
|
5831
|
-
return cacheFiles;
|
|
5832
|
-
}
|
|
5833
|
-
Object.assign(
|
|
5834
|
-
cacheFiles,
|
|
5835
|
-
await (0, import_build_utils13.glob)("**/.vercel/python/.venv/**", { cwd: root, ignore })
|
|
5836
|
-
);
|
|
5837
|
-
Object.assign(
|
|
5838
|
-
cacheFiles,
|
|
5839
|
-
await (0, import_build_utils13.glob)("**/.vercel/python/cache/uv/**", { cwd: root, ignore })
|
|
5840
|
-
);
|
|
5841
|
-
return cacheFiles;
|
|
5842
|
-
};
|
|
5843
5865
|
var shouldServe = (opts) => {
|
|
5844
5866
|
const framework = opts.config.framework;
|
|
5845
5867
|
if ((0, import_build_utils13.isPythonFramework)(framework)) {
|
|
@@ -5877,7 +5899,6 @@ function hasProp(obj, key) {
|
|
|
5877
5899
|
downloadFilesInWorkPath,
|
|
5878
5900
|
installRequirement,
|
|
5879
5901
|
installRequirementsFile,
|
|
5880
|
-
prepareCache,
|
|
5881
5902
|
runFrameworkHook,
|
|
5882
5903
|
shouldServe,
|
|
5883
5904
|
startDevServer,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.22.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"directory": "packages/python"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@vercel/python-analysis": "0.9.
|
|
18
|
+
"@vercel/python-analysis": "0.9.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@renovatebot/pep440": "4.2.1",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"which": "3.0.0",
|
|
36
36
|
"get-port": "5.1.1",
|
|
37
37
|
"is-port-reachable": "3.1.0",
|
|
38
|
-
"@vercel/build-utils": "13.
|
|
38
|
+
"@vercel/build-utils": "13.8.0",
|
|
39
39
|
"@vercel/error-utils": "2.0.3",
|
|
40
|
-
"@vercel/python-runtime": "0.
|
|
40
|
+
"@vercel/python-runtime": "0.7.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "node ../../utils/build-builder.mjs",
|
package/templates/vc_init_dev.py
CHANGED
|
@@ -1,596 +1,15 @@
|
|
|
1
|
-
# Auto-generated
|
|
2
|
-
# Serves static files from PUBLIC_DIR before delegating to the user app.
|
|
1
|
+
# Auto-generated trampoline used by vercel dev (Python, ASGI/WSGI)
|
|
3
2
|
|
|
4
|
-
import sys
|
|
5
3
|
import os
|
|
6
|
-
import inspect
|
|
7
|
-
import logging
|
|
8
|
-
import logging.config
|
|
9
|
-
from os import path as _p
|
|
10
|
-
from importlib import util as _importlib_util
|
|
11
|
-
import mimetypes
|
|
12
4
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
_YELLOW = "\x1b[33m"
|
|
18
|
-
_GREEN = "\x1b[32m"
|
|
19
|
-
_RED = "\x1b[31m"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _color(text: str, code: str) -> str:
|
|
23
|
-
if _NO_COLOR:
|
|
24
|
-
return text
|
|
25
|
-
return f"{code}{text}{_RESET}"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Configure logging to output DEBUG-WARNING to the stdout
|
|
29
|
-
# and ERROR-CRITICAL to the stderr.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# We need a custom filter for the stdout stream
|
|
33
|
-
# so it won't print anything higher than WARNING.
|
|
34
|
-
class _MaxLevelFilter(logging.Filter):
|
|
35
|
-
def __init__(self, max_level):
|
|
36
|
-
super().__init__()
|
|
37
|
-
self.max_level = max_level
|
|
38
|
-
|
|
39
|
-
def filter(self, record):
|
|
40
|
-
return record.levelno <= self.max_level
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def _build_log_config(loggers, _filter_ref=None) -> dict:
|
|
44
|
-
if _filter_ref is None:
|
|
45
|
-
_filter_ref = "vc_init_dev._MaxLevelFilter"
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
"version": 1,
|
|
49
|
-
"disable_existing_loggers": False,
|
|
50
|
-
"filters": {
|
|
51
|
-
"max_warning": {
|
|
52
|
-
"()": _filter_ref,
|
|
53
|
-
"max_level": logging.WARNING,
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"handlers": {
|
|
57
|
-
"stdout": {
|
|
58
|
-
"class": "logging.StreamHandler",
|
|
59
|
-
"stream": "ext://sys.stdout",
|
|
60
|
-
"filters": ["max_warning"],
|
|
61
|
-
},
|
|
62
|
-
"stderr": {
|
|
63
|
-
"class": "logging.StreamHandler",
|
|
64
|
-
"stream": "ext://sys.stderr",
|
|
65
|
-
"level": "ERROR",
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
"loggers": loggers,
|
|
69
|
-
"root": {
|
|
70
|
-
"handlers": ["stdout", "stderr"],
|
|
71
|
-
"level": "INFO",
|
|
72
|
-
},
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _setup_server_log_routing(logger_name=None):
|
|
77
|
-
loggers = {}
|
|
78
|
-
|
|
79
|
-
if logger_name:
|
|
80
|
-
loggers[logger_name] = {
|
|
81
|
-
"handlers": ["stdout", "stderr"],
|
|
82
|
-
"level": "INFO",
|
|
83
|
-
"propagate": False,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
logging.config.dictConfig(
|
|
87
|
-
_build_log_config(
|
|
88
|
-
loggers=loggers,
|
|
89
|
-
_filter_ref=_MaxLevelFilter,
|
|
90
|
-
),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def _build_uvicorn_log_config(default_fmt=None, access_fmt=None) -> dict:
|
|
95
|
-
try:
|
|
96
|
-
from uvicorn.config import LOGGING_CONFIG # type: ignore
|
|
97
|
-
|
|
98
|
-
uvicorn_fmts = LOGGING_CONFIG["formatters"]
|
|
99
|
-
except ImportError:
|
|
100
|
-
uvicorn_fmts = {
|
|
101
|
-
"default": {
|
|
102
|
-
"()": "uvicorn.logging.DefaultFormatter",
|
|
103
|
-
"fmt": "%(levelprefix)s %(message)s",
|
|
104
|
-
"use_colors": None,
|
|
105
|
-
},
|
|
106
|
-
"access": {
|
|
107
|
-
"()": "uvicorn.logging.AccessFormatter",
|
|
108
|
-
"fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
|
|
109
|
-
},
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
cfg = _build_log_config(
|
|
113
|
-
loggers={
|
|
114
|
-
"uvicorn": {
|
|
115
|
-
"handlers": ["stdout", "stderr"],
|
|
116
|
-
"level": "INFO",
|
|
117
|
-
"propagate": False,
|
|
118
|
-
},
|
|
119
|
-
"uvicorn.error": {"level": "INFO"},
|
|
120
|
-
"uvicorn.access": {
|
|
121
|
-
"handlers": ["access"],
|
|
122
|
-
"level": "INFO",
|
|
123
|
-
"propagate": False,
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
if default_fmt is None:
|
|
129
|
-
default_fmt = {**uvicorn_fmts["default"], "use_colors": not _NO_COLOR}
|
|
130
|
-
|
|
131
|
-
if access_fmt is None:
|
|
132
|
-
access_fmt = {**uvicorn_fmts["access"], "use_colors": not _NO_COLOR}
|
|
133
|
-
|
|
134
|
-
cfg["formatters"] = {"default": default_fmt, "access": access_fmt}
|
|
135
|
-
cfg["handlers"]["stdout"]["formatter"] = "default"
|
|
136
|
-
cfg["handlers"]["stderr"]["formatter"] = "default"
|
|
137
|
-
cfg["handlers"]["access"] = {
|
|
138
|
-
"class": "logging.StreamHandler",
|
|
139
|
-
"stream": "ext://sys.stdout",
|
|
140
|
-
"formatter": "access",
|
|
5
|
+
os.environ.update(
|
|
6
|
+
{
|
|
7
|
+
"VERCEL_DEV_MODULE_NAME": "__VC_DEV_MODULE_NAME__",
|
|
8
|
+
"VERCEL_DEV_ENTRY_ABS": "__VC_DEV_ENTRY_ABS__",
|
|
141
9
|
}
|
|
142
|
-
|
|
143
|
-
return cfg
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def _build_hypercorn_log_config():
|
|
147
|
-
return _build_log_config(
|
|
148
|
-
loggers={
|
|
149
|
-
"hypercorn.error": {
|
|
150
|
-
"handlers": ["stdout", "stderr"],
|
|
151
|
-
"level": "INFO",
|
|
152
|
-
"propagate": False,
|
|
153
|
-
},
|
|
154
|
-
"hypercorn.access": {
|
|
155
|
-
"handlers": ["stdout"],
|
|
156
|
-
"level": "INFO",
|
|
157
|
-
"propagate": False,
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def _patch_fastapi_cli_log_config():
|
|
164
|
-
try:
|
|
165
|
-
import fastapi_cli.utils.cli as _fcli # type: ignore
|
|
166
|
-
import fastapi_cli.cli as _fcli_cli # type: ignore
|
|
167
|
-
|
|
168
|
-
_orig_get_config = _fcli.get_uvicorn_log_config # to ensure it's there
|
|
169
|
-
_fcli_cli.get_uvicorn_log_config # to ensure it's there
|
|
170
|
-
except (ImportError, AttributeError):
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
def _get_routed_config():
|
|
174
|
-
orig = _orig_get_config()
|
|
175
|
-
return _build_uvicorn_log_config(
|
|
176
|
-
default_fmt={
|
|
177
|
-
"()": "fastapi_cli.utils.cli.CustomFormatter",
|
|
178
|
-
"fmt": orig["formatters"]["default"].get(
|
|
179
|
-
"fmt", "%(levelprefix)s %(message)s"
|
|
180
|
-
),
|
|
181
|
-
"use_colors": orig["formatters"]["default"].get("use_colors"),
|
|
182
|
-
},
|
|
183
|
-
access_fmt={
|
|
184
|
-
"()": "fastapi_cli.utils.cli.CustomFormatter",
|
|
185
|
-
"fmt": orig["formatters"]["access"].get(
|
|
186
|
-
"fmt",
|
|
187
|
-
"%(levelprefix)s %(client_addr)s - '%(request_line)s' %(status_code)s",
|
|
188
|
-
),
|
|
189
|
-
},
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
_fcli.get_uvicorn_log_config = _get_routed_config
|
|
193
|
-
# we need to patch the local binding as well
|
|
194
|
-
_fcli_cli.get_uvicorn_log_config = _get_routed_config
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def _normalize_service_route_prefix(raw_prefix):
|
|
198
|
-
if not raw_prefix:
|
|
199
|
-
return ""
|
|
200
|
-
|
|
201
|
-
prefix = raw_prefix.strip()
|
|
202
|
-
if not prefix:
|
|
203
|
-
return ""
|
|
204
|
-
|
|
205
|
-
if not prefix.startswith("/"):
|
|
206
|
-
prefix = f"/{prefix}"
|
|
207
|
-
|
|
208
|
-
return "" if prefix == "/" else prefix.rstrip("/")
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def _is_service_route_prefix_strip_enabled():
|
|
212
|
-
raw = os.environ.get("VERCEL_SERVICE_ROUTE_PREFIX_STRIP")
|
|
213
|
-
if not raw:
|
|
214
|
-
return False
|
|
215
|
-
return raw.lower() in ("1", "true")
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
_SERVICE_ROUTE_PREFIX = (
|
|
219
|
-
_normalize_service_route_prefix(os.environ.get("VERCEL_SERVICE_ROUTE_PREFIX"))
|
|
220
|
-
if _is_service_route_prefix_strip_enabled()
|
|
221
|
-
else ""
|
|
222
10
|
)
|
|
223
11
|
|
|
224
|
-
|
|
225
|
-
def _strip_service_route_prefix(path_value):
|
|
226
|
-
if not path_value:
|
|
227
|
-
path_value = "/"
|
|
228
|
-
elif not path_value.startswith("/"):
|
|
229
|
-
path_value = f"/{path_value}"
|
|
230
|
-
|
|
231
|
-
prefix = _SERVICE_ROUTE_PREFIX
|
|
232
|
-
if not prefix:
|
|
233
|
-
return path_value, ""
|
|
234
|
-
|
|
235
|
-
if path_value == prefix:
|
|
236
|
-
return "/", prefix
|
|
237
|
-
|
|
238
|
-
if path_value.startswith(f"{prefix}/"):
|
|
239
|
-
stripped = path_value[len(prefix) :]
|
|
240
|
-
return stripped if stripped else "/", prefix
|
|
241
|
-
|
|
242
|
-
return path_value, ""
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# Pre-configure the root logger before user module import so that any log
|
|
246
|
-
# calls emitted at import time are routed to stdout/stderr correctly.
|
|
247
|
-
_setup_server_log_routing()
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
# ASGI/WSGI app detection
|
|
251
|
-
_MODULE_NAME = "__VC_DEV_MODULE_NAME__"
|
|
252
|
-
_ENTRY_ABS = "__VC_DEV_ENTRY_ABS__"
|
|
253
|
-
|
|
254
|
-
# Import user module by file path, matching vc_init.py's approach.
|
|
255
|
-
# https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
|
|
256
|
-
_spec = _importlib_util.spec_from_file_location(_MODULE_NAME, _ENTRY_ABS)
|
|
257
|
-
if _spec is None or _spec.loader is None:
|
|
258
|
-
raise RuntimeError(
|
|
259
|
-
f"Could not load module spec for '{_MODULE_NAME}' at {_ENTRY_ABS}"
|
|
260
|
-
)
|
|
261
|
-
_mod = _importlib_util.module_from_spec(_spec)
|
|
262
|
-
sys.modules[_MODULE_NAME] = _mod
|
|
263
|
-
_spec.loader.exec_module(_mod)
|
|
264
|
-
|
|
265
|
-
_user_app_name = (
|
|
266
|
-
"app"
|
|
267
|
-
if hasattr(_mod, "app")
|
|
268
|
-
else "application"
|
|
269
|
-
if hasattr(_mod, "application")
|
|
270
|
-
else None
|
|
271
|
-
)
|
|
272
|
-
if _user_app_name is None:
|
|
273
|
-
raise RuntimeError(
|
|
274
|
-
f"Missing 'app' or 'application' in module '{_MODULE_NAME}'. "
|
|
275
|
-
f"Define `app = ...` or `application = ...` in your entrypoint."
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
_user_app = getattr(_mod, _user_app_name, None)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
def _get_positional_param_count(obj):
|
|
282
|
-
try:
|
|
283
|
-
sig = inspect.signature(obj)
|
|
284
|
-
return sum(
|
|
285
|
-
1
|
|
286
|
-
for p in sig.parameters.values()
|
|
287
|
-
if p.default is inspect.Parameter.empty
|
|
288
|
-
and p.kind
|
|
289
|
-
in (
|
|
290
|
-
inspect.Parameter.POSITIONAL_ONLY,
|
|
291
|
-
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
292
|
-
)
|
|
293
|
-
)
|
|
294
|
-
except (ValueError, TypeError):
|
|
295
|
-
return None
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
def _detect_app_type(app_obj):
|
|
299
|
-
# try .asgi attribute if it's available and is callable
|
|
300
|
-
asgi_attr = getattr(app_obj, "asgi", None)
|
|
301
|
-
if asgi_attr is not None and callable(asgi_attr):
|
|
302
|
-
return "asgi", asgi_attr
|
|
303
|
-
|
|
304
|
-
# For async detection, check the object itself first (works for plain
|
|
305
|
-
# functions/methods).
|
|
306
|
-
# For class instances iscoroutinefunction(obj) is False,
|
|
307
|
-
# so fall back to __call__.
|
|
308
|
-
is_async = inspect.iscoroutinefunction(app_obj)
|
|
309
|
-
if not is_async:
|
|
310
|
-
call_method = getattr(app_obj, "__call__", None)
|
|
311
|
-
if call_method is not None:
|
|
312
|
-
is_async = inspect.iscoroutinefunction(call_method)
|
|
313
|
-
|
|
314
|
-
# inspect.signature() already delegates to __call__ for class instances,
|
|
315
|
-
# and works directly on plain functions, so always inspect app_obj.
|
|
316
|
-
param_count = _get_positional_param_count(app_obj)
|
|
317
|
-
|
|
318
|
-
# ASGI (scope, receive, send)
|
|
319
|
-
if is_async and param_count == 3:
|
|
320
|
-
return "asgi", app_obj
|
|
321
|
-
|
|
322
|
-
# WSGI (environ, start_response)
|
|
323
|
-
if param_count == 2:
|
|
324
|
-
return "wsgi", app_obj
|
|
325
|
-
|
|
326
|
-
print(
|
|
327
|
-
_color(
|
|
328
|
-
f"Could not determine the application interface for '{_MODULE_NAME}:{_user_app_name}'\n"
|
|
329
|
-
f"Expected either:\n"
|
|
330
|
-
f" - An ASGI app: async callable(scope, receive, send)\n"
|
|
331
|
-
f" - A WSGI app: callable(environ, start_response)",
|
|
332
|
-
_RED,
|
|
333
|
-
),
|
|
334
|
-
file=sys.stderr,
|
|
335
|
-
)
|
|
336
|
-
sys.exit(1)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
app_type, resolved_app = _detect_app_type(_user_app)
|
|
340
|
-
|
|
341
|
-
if app_type == "asgi":
|
|
342
|
-
_asgi_app = resolved_app
|
|
343
|
-
_wsgi_app = None
|
|
344
|
-
else:
|
|
345
|
-
_wsgi_app = resolved_app
|
|
346
|
-
_asgi_app = None
|
|
347
|
-
|
|
348
|
-
PUBLIC_DIR = "public"
|
|
349
|
-
|
|
350
|
-
# Prepare ASGI app
|
|
351
|
-
|
|
352
|
-
# Optional StaticFiles import; tolerate missing deps
|
|
353
|
-
StaticFiles = None
|
|
354
|
-
try:
|
|
355
|
-
from fastapi.staticfiles import StaticFiles as _SF # type: ignore
|
|
356
|
-
|
|
357
|
-
StaticFiles = _SF
|
|
358
|
-
except Exception:
|
|
359
|
-
try:
|
|
360
|
-
from starlette.staticfiles import StaticFiles as _SF # type: ignore
|
|
361
|
-
|
|
362
|
-
StaticFiles = _SF
|
|
363
|
-
except Exception:
|
|
364
|
-
StaticFiles = None
|
|
365
|
-
|
|
366
|
-
# Prepare static files app (if starlette/fastapi installed)
|
|
367
|
-
_static_app = None
|
|
368
|
-
if StaticFiles is not None:
|
|
369
|
-
try:
|
|
370
|
-
try:
|
|
371
|
-
_static_app = StaticFiles(directory=PUBLIC_DIR, check_dir=False)
|
|
372
|
-
except TypeError:
|
|
373
|
-
_static_app = StaticFiles(directory=PUBLIC_DIR)
|
|
374
|
-
except Exception:
|
|
375
|
-
_static_app = None
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
def _apply_service_route_prefix_to_scope(scope):
|
|
379
|
-
path_value, matched_prefix = _strip_service_route_prefix(scope.get("path", "/"))
|
|
380
|
-
if not matched_prefix:
|
|
381
|
-
return scope
|
|
382
|
-
|
|
383
|
-
updated_scope = dict(scope)
|
|
384
|
-
updated_scope["path"] = path_value
|
|
385
|
-
|
|
386
|
-
raw_path = scope.get("raw_path")
|
|
387
|
-
if isinstance(raw_path, (bytes, bytearray)):
|
|
388
|
-
try:
|
|
389
|
-
decoded = bytes(raw_path).decode("utf-8", "surrogateescape")
|
|
390
|
-
stripped_raw, _ = _strip_service_route_prefix(decoded)
|
|
391
|
-
updated_scope["raw_path"] = stripped_raw.encode("utf-8", "surrogateescape")
|
|
392
|
-
except Exception:
|
|
393
|
-
pass
|
|
394
|
-
|
|
395
|
-
existing_root = scope.get("root_path", "") or ""
|
|
396
|
-
if existing_root and existing_root != "/":
|
|
397
|
-
existing_root = existing_root.rstrip("/")
|
|
398
|
-
else:
|
|
399
|
-
existing_root = ""
|
|
400
|
-
updated_scope["root_path"] = f"{existing_root}{matched_prefix}"
|
|
401
|
-
return updated_scope
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
async def asgi_app(scope, receive, send):
|
|
405
|
-
effective_scope = _apply_service_route_prefix_to_scope(scope)
|
|
406
|
-
|
|
407
|
-
if _static_app is not None and effective_scope.get("type") == "http":
|
|
408
|
-
req_path = effective_scope.get("path", "/") or "/"
|
|
409
|
-
safe = _p.normpath(req_path).lstrip("/")
|
|
410
|
-
full = _p.join(PUBLIC_DIR, safe)
|
|
411
|
-
try:
|
|
412
|
-
base = _p.realpath(PUBLIC_DIR)
|
|
413
|
-
target = _p.realpath(full)
|
|
414
|
-
if (target == base or target.startswith(base + _p.sep)) and _p.isfile(
|
|
415
|
-
target
|
|
416
|
-
):
|
|
417
|
-
await _static_app(effective_scope, receive, send)
|
|
418
|
-
return
|
|
419
|
-
except Exception:
|
|
420
|
-
pass
|
|
421
|
-
await _asgi_app(effective_scope, receive, send)
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
# Prepare WSGI
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
def _is_safe_file(base_dir: str, target: str) -> bool:
|
|
428
|
-
try:
|
|
429
|
-
base = _p.realpath(base_dir)
|
|
430
|
-
tgt = _p.realpath(target)
|
|
431
|
-
return (tgt == base or tgt.startswith(base + os.sep)) and _p.isfile(tgt)
|
|
432
|
-
except Exception:
|
|
433
|
-
return False
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
def _static_wsgi_app(environ, start_response):
|
|
437
|
-
# Only handle GET/HEAD requests for static assets
|
|
438
|
-
if environ.get("REQUEST_METHOD", "GET") not in ("GET", "HEAD"):
|
|
439
|
-
return _not_found(start_response)
|
|
440
|
-
|
|
441
|
-
req_path = environ.get("PATH_INFO", "/") or "/"
|
|
442
|
-
safe = _p.normpath(req_path).lstrip("/")
|
|
443
|
-
full = _p.join(PUBLIC_DIR, safe)
|
|
444
|
-
if not _is_safe_file(PUBLIC_DIR, full):
|
|
445
|
-
return _not_found(start_response)
|
|
446
|
-
|
|
447
|
-
ctype, _ = mimetypes.guess_type(full)
|
|
448
|
-
headers = [("Content-Type", ctype or "application/octet-stream")]
|
|
449
|
-
try:
|
|
450
|
-
# For HEAD requests, send headers only
|
|
451
|
-
if environ.get("REQUEST_METHOD") == "HEAD":
|
|
452
|
-
start_response("200 OK", headers)
|
|
453
|
-
return []
|
|
454
|
-
with open(full, "rb") as f:
|
|
455
|
-
data = f.read()
|
|
456
|
-
headers.append(("Content-Length", str(len(data))))
|
|
457
|
-
start_response("200 OK", headers)
|
|
458
|
-
return [data]
|
|
459
|
-
except Exception:
|
|
460
|
-
return _not_found(start_response)
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
def _not_found(start_response):
|
|
464
|
-
start_response("404 Not Found", [("Content-Type", "text/plain; charset=utf-8")])
|
|
465
|
-
return [b"Not Found"]
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
def wsgi_app(environ, start_response):
|
|
469
|
-
path_info, matched_prefix = _strip_service_route_prefix(
|
|
470
|
-
environ.get("PATH_INFO", "/") or "/"
|
|
471
|
-
)
|
|
472
|
-
environ["PATH_INFO"] = path_info
|
|
473
|
-
if matched_prefix:
|
|
474
|
-
script_name = environ.get("SCRIPT_NAME", "") or ""
|
|
475
|
-
if script_name and script_name != "/":
|
|
476
|
-
script_name = script_name.rstrip("/")
|
|
477
|
-
else:
|
|
478
|
-
script_name = ""
|
|
479
|
-
environ["SCRIPT_NAME"] = f"{script_name}{matched_prefix}"
|
|
480
|
-
|
|
481
|
-
# Try static first; if 404 then delegate to user app
|
|
482
|
-
captured_status = ""
|
|
483
|
-
captured_headers = tuple()
|
|
484
|
-
body_chunks = []
|
|
485
|
-
|
|
486
|
-
def capture_start_response(status, headers, exc_info=None):
|
|
487
|
-
nonlocal captured_status, captured_headers
|
|
488
|
-
captured_status = status
|
|
489
|
-
captured_headers = tuple(headers)
|
|
490
|
-
|
|
491
|
-
# Return a writer that buffers the body
|
|
492
|
-
def write(chunk: bytes):
|
|
493
|
-
body_chunks.append(chunk)
|
|
494
|
-
|
|
495
|
-
return write
|
|
496
|
-
|
|
497
|
-
result = _static_wsgi_app(environ, capture_start_response)
|
|
498
|
-
# If static handler produced 200, forward its response
|
|
499
|
-
if captured_status.startswith("200 "):
|
|
500
|
-
# Send headers and any chunks collected
|
|
501
|
-
writer = start_response(captured_status, list(captured_headers))
|
|
502
|
-
for chunk in body_chunks:
|
|
503
|
-
writer(chunk)
|
|
504
|
-
return result
|
|
505
|
-
|
|
506
|
-
# Otherwise, delegate to user's WSGI app
|
|
507
|
-
return _wsgi_app(environ, start_response)
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
# Run dev server
|
|
12
|
+
from vercel_runtime.dev import main # noqa: E402
|
|
511
13
|
|
|
512
14
|
if __name__ == "__main__":
|
|
513
|
-
|
|
514
|
-
#
|
|
515
|
-
# For WSGI: prefer Werkzeug, but fall back to stdlib wsgiref.
|
|
516
|
-
# For ASGI: prefer FastAPI CLI (dev command), then uvicorn, then hypercorn.
|
|
517
|
-
#
|
|
518
|
-
# The port is provided by the caller via the PORT environment variable.
|
|
519
|
-
|
|
520
|
-
host = "127.0.0.1"
|
|
521
|
-
_raw_port = os.environ.get("PORT")
|
|
522
|
-
if not _raw_port:
|
|
523
|
-
print(
|
|
524
|
-
_color("PORT environment variable is required.", _RED),
|
|
525
|
-
file=sys.stderr,
|
|
526
|
-
)
|
|
527
|
-
sys.exit(1)
|
|
528
|
-
port = int(_raw_port)
|
|
529
|
-
|
|
530
|
-
if app_type == "wsgi":
|
|
531
|
-
try:
|
|
532
|
-
from werkzeug.serving import run_simple # type: ignore
|
|
533
|
-
|
|
534
|
-
_setup_server_log_routing("werkzeug")
|
|
535
|
-
run_simple(host, port, wsgi_app, use_reloader=True, threaded=True)
|
|
536
|
-
except Exception:
|
|
537
|
-
print(
|
|
538
|
-
_color(
|
|
539
|
-
"Werkzeug not installed; falling back to wsgiref (no reloader).",
|
|
540
|
-
_YELLOW,
|
|
541
|
-
),
|
|
542
|
-
file=sys.stderr,
|
|
543
|
-
)
|
|
544
|
-
from wsgiref.simple_server import make_server
|
|
545
|
-
|
|
546
|
-
httpd = make_server(host, port, wsgi_app)
|
|
547
|
-
print(_color(f"Serving on http://{host}:{port}", _GREEN))
|
|
548
|
-
httpd.serve_forever()
|
|
549
|
-
else:
|
|
550
|
-
try:
|
|
551
|
-
from fastapi_cli.cli import dev as fastapi_dev # type: ignore
|
|
552
|
-
except ImportError:
|
|
553
|
-
fastapi_dev = None
|
|
554
|
-
|
|
555
|
-
if fastapi_dev is not None:
|
|
556
|
-
_patch_fastapi_cli_log_config()
|
|
557
|
-
fastapi_dev(
|
|
558
|
-
entrypoint="vc_init_dev:asgi_app", host=host, port=port, reload=True
|
|
559
|
-
)
|
|
560
|
-
sys.exit(0)
|
|
561
|
-
|
|
562
|
-
try:
|
|
563
|
-
import uvicorn # type: ignore
|
|
564
|
-
|
|
565
|
-
uvicorn.run(
|
|
566
|
-
"vc_init_dev:asgi_app",
|
|
567
|
-
host=host,
|
|
568
|
-
port=port,
|
|
569
|
-
use_colors=True,
|
|
570
|
-
reload=True,
|
|
571
|
-
log_config=_build_uvicorn_log_config(),
|
|
572
|
-
)
|
|
573
|
-
except Exception:
|
|
574
|
-
try:
|
|
575
|
-
import asyncio
|
|
576
|
-
from hypercorn.config import Config # type: ignore
|
|
577
|
-
from hypercorn.asyncio import serve # type: ignore
|
|
578
|
-
|
|
579
|
-
config = Config()
|
|
580
|
-
config.bind = [f"{host}:{port}"]
|
|
581
|
-
config.use_reloader = True
|
|
582
|
-
config.logconfig_dict = _build_hypercorn_log_config()
|
|
583
|
-
|
|
584
|
-
async def _run():
|
|
585
|
-
await serve(asgi_app, config)
|
|
586
|
-
|
|
587
|
-
asyncio.run(_run())
|
|
588
|
-
except Exception:
|
|
589
|
-
print(
|
|
590
|
-
_color(
|
|
591
|
-
'No ASGI server found. Please install either "uvicorn" or "hypercorn" (e.g. "pip install uvicorn").',
|
|
592
|
-
_RED,
|
|
593
|
-
),
|
|
594
|
-
file=sys.stderr,
|
|
595
|
-
)
|
|
596
|
-
sys.exit(1)
|
|
15
|
+
main()
|