@vercel/python 5.0.7 → 5.0.8
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 +61 -0
- package/package.json +2 -2
- package/vc_init.py +9 -4
package/dist/index.js
CHANGED
|
@@ -3224,6 +3224,47 @@ function isFastapiEntrypoint(file) {
|
|
|
3224
3224
|
return false;
|
|
3225
3225
|
}
|
|
3226
3226
|
}
|
|
3227
|
+
var FLASK_ENTRYPOINT_FILENAMES = ["app", "index", "server", "main"];
|
|
3228
|
+
var FLASK_ENTRYPOINT_DIRS = ["", "src", "app"];
|
|
3229
|
+
var FLASK_CONTENT_REGEX = /(from\s+flask\s+import\s+Flask|import\s+flask|Flask\s*\()/;
|
|
3230
|
+
var FLASK_CANDIDATE_ENTRYPOINTS = FLASK_ENTRYPOINT_FILENAMES.flatMap(
|
|
3231
|
+
(filename) => FLASK_ENTRYPOINT_DIRS.map(
|
|
3232
|
+
(dir) => import_path2.posix.join(dir, `${filename}.py`)
|
|
3233
|
+
)
|
|
3234
|
+
);
|
|
3235
|
+
function isFlaskEntrypoint(file) {
|
|
3236
|
+
try {
|
|
3237
|
+
const fsPath = file.fsPath;
|
|
3238
|
+
if (!fsPath)
|
|
3239
|
+
return false;
|
|
3240
|
+
const contents = import_fs2.default.readFileSync(fsPath, "utf8");
|
|
3241
|
+
return FLASK_CONTENT_REGEX.test(contents);
|
|
3242
|
+
} catch {
|
|
3243
|
+
return false;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
async function detectFlaskEntrypoint(workPath, configuredEntrypoint) {
|
|
3247
|
+
const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
|
|
3248
|
+
try {
|
|
3249
|
+
const fsFiles = await (0, import_build_utils3.glob)("**", workPath);
|
|
3250
|
+
if (fsFiles[entry])
|
|
3251
|
+
return entry;
|
|
3252
|
+
const candidates = FLASK_CANDIDATE_ENTRYPOINTS.filter(
|
|
3253
|
+
(c) => !!fsFiles[c]
|
|
3254
|
+
);
|
|
3255
|
+
if (candidates.length > 0) {
|
|
3256
|
+
const flaskEntrypoint = candidates.find(
|
|
3257
|
+
(c) => isFlaskEntrypoint(fsFiles[c])
|
|
3258
|
+
) || candidates[0];
|
|
3259
|
+
(0, import_build_utils3.debug)(`Detected Flask entrypoint: ${flaskEntrypoint}`);
|
|
3260
|
+
return flaskEntrypoint;
|
|
3261
|
+
}
|
|
3262
|
+
return null;
|
|
3263
|
+
} catch {
|
|
3264
|
+
(0, import_build_utils3.debug)("Failed to discover entrypoint for Flask");
|
|
3265
|
+
return null;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3227
3268
|
async function detectFastapiEntrypoint(workPath, configuredEntrypoint) {
|
|
3228
3269
|
const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
|
|
3229
3270
|
try {
|
|
@@ -3645,6 +3686,20 @@ var build = async ({
|
|
|
3645
3686
|
message: `No FastAPI entrypoint found. Searched for: ${searchedList}`
|
|
3646
3687
|
});
|
|
3647
3688
|
}
|
|
3689
|
+
} else if (!fsFiles[entrypoint] && config?.framework === "flask") {
|
|
3690
|
+
const detected = await detectFlaskEntrypoint(workPath, entrypoint);
|
|
3691
|
+
if (detected) {
|
|
3692
|
+
(0, import_build_utils5.debug)(
|
|
3693
|
+
`Resolved Python entrypoint to "${detected}" (configured "${entrypoint}" not found).`
|
|
3694
|
+
);
|
|
3695
|
+
entrypoint = detected;
|
|
3696
|
+
} else {
|
|
3697
|
+
const searchedList = FLASK_CANDIDATE_ENTRYPOINTS.join(", ");
|
|
3698
|
+
throw new import_build_utils5.NowBuildError({
|
|
3699
|
+
code: "FLASK_ENTRYPOINT_NOT_FOUND",
|
|
3700
|
+
message: `No Flask entrypoint found. Searched for: ${searchedList}`
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3648
3703
|
}
|
|
3649
3704
|
const entryDirectory = (0, import_path5.dirname)(entrypoint);
|
|
3650
3705
|
const hasReqLocal = !!fsFiles[(0, import_path5.join)(entryDirectory, "requirements.txt")];
|
|
@@ -3915,6 +3970,12 @@ var shouldServe = (opts) => {
|
|
|
3915
3970
|
return false;
|
|
3916
3971
|
}
|
|
3917
3972
|
return true;
|
|
3973
|
+
} else if (framework === "flask") {
|
|
3974
|
+
const requestPath = opts.requestPath.replace(/\/$/, "");
|
|
3975
|
+
if (requestPath.startsWith("api") && opts.hasMatched) {
|
|
3976
|
+
return false;
|
|
3977
|
+
}
|
|
3978
|
+
return true;
|
|
3918
3979
|
}
|
|
3919
3980
|
return defaultShouldServe(opts);
|
|
3920
3981
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@types/jest": "27.4.1",
|
|
21
21
|
"@types/node": "14.18.33",
|
|
22
22
|
"@types/which": "3.0.0",
|
|
23
|
-
"@vercel/build-utils": "12.1.
|
|
23
|
+
"@vercel/build-utils": "12.1.2",
|
|
24
24
|
"cross-env": "7.0.3",
|
|
25
25
|
"execa": "^1.0.0",
|
|
26
26
|
"fs-extra": "11.1.1",
|
package/vc_init.py
CHANGED
|
@@ -434,6 +434,8 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
434
434
|
|
|
435
435
|
# Event to signal that the response has been fully sent
|
|
436
436
|
response_done = threading.Event()
|
|
437
|
+
# Event to signal the ASGI app has fully completed (incl. background tasks)
|
|
438
|
+
app_done = threading.Event()
|
|
437
439
|
|
|
438
440
|
# Propagate request context to background thread for logging & metrics
|
|
439
441
|
request_context = storage.get()
|
|
@@ -488,6 +490,8 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
488
490
|
# Run ASGI app (includes background tasks)
|
|
489
491
|
asgi_instance = app(scope, receive, send)
|
|
490
492
|
await asgi_instance
|
|
493
|
+
# Mark app completion when the ASGI callable returns
|
|
494
|
+
app_done.set()
|
|
491
495
|
|
|
492
496
|
asyncio.run(runner())
|
|
493
497
|
except Exception:
|
|
@@ -504,10 +508,9 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
504
508
|
pass
|
|
505
509
|
finally:
|
|
506
510
|
# Always unblock the waiting thread to avoid hangs
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
pass
|
|
511
|
+
response_done.set()
|
|
512
|
+
# Ensure app completion is always signaled
|
|
513
|
+
app_done.set()
|
|
511
514
|
if token is not None:
|
|
512
515
|
storage.reset(token)
|
|
513
516
|
|
|
@@ -517,6 +520,8 @@ if 'VERCEL_IPC_PATH' in os.environ:
|
|
|
517
520
|
|
|
518
521
|
# Wait until final body chunk has been flushed to client
|
|
519
522
|
response_done.wait()
|
|
523
|
+
# Also wait until the ASGI app finishes (includes background tasks)
|
|
524
|
+
app_done.wait()
|
|
520
525
|
|
|
521
526
|
if 'Handler' in locals():
|
|
522
527
|
server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
|