@vercel/python 6.37.0 → 6.38.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 +264 -46
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3231,7 +3231,7 @@ var import_path13 = require("path");
|
|
|
3231
3231
|
|
|
3232
3232
|
// src/package-versions.ts
|
|
3233
3233
|
var VERCEL_RUNTIME_VERSION = "0.13.1";
|
|
3234
|
-
var VERCEL_WORKERS_VERSION = "0.0.
|
|
3234
|
+
var VERCEL_WORKERS_VERSION = "0.0.22";
|
|
3235
3235
|
|
|
3236
3236
|
// src/index.ts
|
|
3237
3237
|
var import_build_utils16 = require("@vercel/build-utils");
|
|
@@ -4328,6 +4328,9 @@ var LAMBDA_SIZE_THRESHOLD_BYTES = 245 * 1024 * 1024;
|
|
|
4328
4328
|
var LAMBDA_EPHEMERAL_STORAGE_BYTES = 500 * 1024 * 1024;
|
|
4329
4329
|
var HIVE_LAMBDA_SIZE_BYTES = 1 * 1024 * 1024 * 1024;
|
|
4330
4330
|
var FUNCTIONS_BETA_CTA = "Run `vercel deploy --functions-beta` to use extended function limits (up to 1 GB), or reduce your dependency footprint.";
|
|
4331
|
+
var BUNDLING_DOCS_LINK = "https://vercel.com/docs/functions/runtimes/python#controlling-what-gets-bundled";
|
|
4332
|
+
var FUNCTIONS_BETA_DOCS_LINK = "https://vercel.com/docs/functions/runtimes/python#extended-size-limits-with-functions-beta";
|
|
4333
|
+
var FUNCTIONS_BETA_EXCEEDED_CTA = "Your deployment is already using extended function limits (`--functions-beta`).\nReduce your dependency footprint to under 1 GB or split your application\ninto smaller functions.";
|
|
4331
4334
|
function shouldShowFunctionsBetaHint() {
|
|
4332
4335
|
const v = process.env.VERCEL_FUNCTIONS_BETA_HINT;
|
|
4333
4336
|
return v === "1" || v === "true";
|
|
@@ -4390,10 +4393,13 @@ var PythonDependencyExternalizer = class {
|
|
|
4390
4393
|
|
|
4391
4394
|
` + FUNCTIONS_BETA_CTA : `Total bundle size (${totalBundleSizeMB} MB) exceeds the size limit (${limitMB} MB).
|
|
4392
4395
|
|
|
4393
|
-
When using a custom install command, Vercel cannot automatically
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4396
|
+
When using a custom install command, Vercel cannot automatically
|
|
4397
|
+
optimize dependency bundling. To reduce the size of your
|
|
4398
|
+
dependencies, you can:
|
|
4399
|
+
1. Remove unused dependencies from your project.
|
|
4400
|
+
2. Remove the custom install command to allow Vercel to manage
|
|
4401
|
+
and optimize dependencies automatically.`,
|
|
4402
|
+
link: shouldShowFunctionsBetaHint() ? FUNCTIONS_BETA_DOCS_LINK : BUNDLING_DOCS_LINK,
|
|
4397
4403
|
action: "Learn More"
|
|
4398
4404
|
});
|
|
4399
4405
|
}
|
|
@@ -4401,7 +4407,12 @@ When using a custom install command, Vercel cannot automatically optimize depend
|
|
|
4401
4407
|
const limitMB = (HIVE_LAMBDA_SIZE_BYTES / (1024 * 1024)).toFixed(0);
|
|
4402
4408
|
throw new import_build_utils5.NowBuildError({
|
|
4403
4409
|
code: "LAMBDA_SIZE_EXCEEDED",
|
|
4404
|
-
message: `Total bundle size (${totalBundleSizeMB} MB) exceeds the extended function size limit (${limitMB} MB).
|
|
4410
|
+
message: shouldShowFunctionsBetaHint() ? `Total bundle size (${totalBundleSizeMB} MB) exceeds the extended function size limit (${limitMB} MB).
|
|
4411
|
+
|
|
4412
|
+
` + FUNCTIONS_BETA_EXCEEDED_CTA : `Total bundle size (${totalBundleSizeMB} MB) exceeds the extended function size limit (${limitMB} MB).
|
|
4413
|
+
|
|
4414
|
+
Consider removing unused dependencies or splitting your
|
|
4415
|
+
application into smaller functions.`,
|
|
4405
4416
|
link: "https://vercel.com/docs/functions/runtimes/python#controlling-what-gets-bundled",
|
|
4406
4417
|
action: "Learn More"
|
|
4407
4418
|
});
|
|
@@ -4440,8 +4451,13 @@ When using a custom install command, Vercel cannot automatically optimize depend
|
|
|
4440
4451
|
code: "LAMBDA_SIZE_EXCEEDED",
|
|
4441
4452
|
message: shouldShowFunctionsBetaHint() ? `Total bundle size (${totalBundleSizeMB} MB) exceeds the ephemeral storage limit (${ephemeralLimitMB} MB).
|
|
4442
4453
|
|
|
4443
|
-
` + FUNCTIONS_BETA_CTA : `Total bundle size (${totalBundleSizeMB} MB) exceeds Lambda ephemeral storage limit (${ephemeralLimitMB} MB).
|
|
4444
|
-
|
|
4454
|
+
` + FUNCTIONS_BETA_CTA : `Total bundle size (${totalBundleSizeMB} MB) exceeds Lambda ephemeral storage limit (${ephemeralLimitMB} MB).
|
|
4455
|
+
|
|
4456
|
+
Even with runtime dependency installation, all packages must fit
|
|
4457
|
+
within the ${ephemeralLimitMB} MB ephemeral storage available to Lambda
|
|
4458
|
+
functions. Consider removing unused dependencies or splitting
|
|
4459
|
+
your application into smaller functions.`,
|
|
4460
|
+
link: shouldShowFunctionsBetaHint() ? FUNCTIONS_BETA_DOCS_LINK : BUNDLING_DOCS_LINK,
|
|
4445
4461
|
action: "Learn More"
|
|
4446
4462
|
});
|
|
4447
4463
|
}
|
|
@@ -4513,12 +4529,17 @@ ${error.fileContent}`
|
|
|
4513
4529
|
if (externalizablePublic.length === 0) {
|
|
4514
4530
|
throw new import_build_utils5.NowBuildError({
|
|
4515
4531
|
code: "RUNTIME_DEPENDENCY_INSTALLATION_FAILED",
|
|
4516
|
-
message: `Bundle size exceeds the Lambda limit and requires runtime
|
|
4517
|
-
|
|
4532
|
+
message: `Bundle size exceeds the Lambda limit and requires runtime
|
|
4533
|
+
dependency installation, but no public packages have compatible
|
|
4534
|
+
pre-built wheels for the Lambda platform.
|
|
4535
|
+
|
|
4536
|
+
Runtime dependency installation requires packages to have binary
|
|
4537
|
+
wheels.
|
|
4518
4538
|
|
|
4519
4539
|
To fix this, either:
|
|
4520
|
-
|
|
4521
|
-
|
|
4540
|
+
1. Regenerate your lock file with: uv lock --upgrade, or
|
|
4541
|
+
2. Switch the problematic packages to ones that have pre-built
|
|
4542
|
+
wheels available.`
|
|
4522
4543
|
});
|
|
4523
4544
|
}
|
|
4524
4545
|
const packageSizes = await calculatePerPackageSizes(this.venvPath);
|
|
@@ -4636,8 +4657,13 @@ To fix this, either:
|
|
|
4636
4657
|
code: "LAMBDA_SIZE_EXCEEDED",
|
|
4637
4658
|
message: shouldShowFunctionsBetaHint() ? `Total bundle size (${finalSizeMB} MB) exceeds the size limit (${limitMB} MB).
|
|
4638
4659
|
|
|
4639
|
-
` + FUNCTIONS_BETA_CTA : `Total bundle size (${finalSizeMB} MB) exceeds Lambda limit (${limitMB} MB) even after
|
|
4640
|
-
|
|
4660
|
+
` + FUNCTIONS_BETA_CTA : `Total bundle size (${finalSizeMB} MB) exceeds Lambda limit (${limitMB} MB) even after
|
|
4661
|
+
deferring public packages to runtime installation.
|
|
4662
|
+
|
|
4663
|
+
This usually means your private packages or source code are too
|
|
4664
|
+
large. Consider reducing the size of private dependencies or
|
|
4665
|
+
splitting your application.`,
|
|
4666
|
+
link: shouldShowFunctionsBetaHint() ? FUNCTIONS_BETA_DOCS_LINK : BUNDLING_DOCS_LINK,
|
|
4641
4667
|
action: "Learn More"
|
|
4642
4668
|
});
|
|
4643
4669
|
}
|
|
@@ -5280,6 +5306,7 @@ var PYTHON_ENTRYPOINT_DIRS = ["", "src", "app", "api"];
|
|
|
5280
5306
|
var PYTHON_CANDIDATE_ENTRYPOINTS = getCandidateEntrypointsInDirs(
|
|
5281
5307
|
PYTHON_ENTRYPOINT_DIRS
|
|
5282
5308
|
);
|
|
5309
|
+
var PYTHON_ENTRYPOINT_DOCS_URL = "https://vercel.com/docs/functions/runtimes/python#python-entrypoints";
|
|
5283
5310
|
function getCandidateEntrypointsInDirs(dirs) {
|
|
5284
5311
|
return dirs.flatMap(
|
|
5285
5312
|
(dir) => PYTHON_ENTRYPOINT_FILENAMES.map(
|
|
@@ -5295,6 +5322,51 @@ async function fileExists(filePath) {
|
|
|
5295
5322
|
return false;
|
|
5296
5323
|
}
|
|
5297
5324
|
}
|
|
5325
|
+
function getFrameworkDisplayName(framework) {
|
|
5326
|
+
switch (framework) {
|
|
5327
|
+
case "fastapi":
|
|
5328
|
+
return "FastAPI";
|
|
5329
|
+
case "flask":
|
|
5330
|
+
return "Flask";
|
|
5331
|
+
case "django":
|
|
5332
|
+
return "Django";
|
|
5333
|
+
case "fasthtml":
|
|
5334
|
+
return "FastHTML";
|
|
5335
|
+
default:
|
|
5336
|
+
return framework;
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
function getFrameworkEntrypointDocsUrl(framework) {
|
|
5340
|
+
switch (framework) {
|
|
5341
|
+
case "fastapi":
|
|
5342
|
+
return "https://vercel.com/docs/frameworks/backend/fastapi#exporting-the-fastapi-application";
|
|
5343
|
+
case "flask":
|
|
5344
|
+
return "https://vercel.com/docs/frameworks/backend/flask#exporting-the-flask-application";
|
|
5345
|
+
case "django":
|
|
5346
|
+
return "https://vercel.com/docs/frameworks/full-stack/django#configure-the-django-entrypoint";
|
|
5347
|
+
default:
|
|
5348
|
+
return PYTHON_ENTRYPOINT_DOCS_URL;
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
function getMissingExportMessage(framework, entrypoints) {
|
|
5352
|
+
const fileList = entrypoints.slice(0, 3).join(", ");
|
|
5353
|
+
const found = `Found ${fileList}`;
|
|
5354
|
+
const subject = entrypoints.length === 1 ? "it" : "none";
|
|
5355
|
+
const verb = entrypoints.length === 1 ? "does not define" : "define";
|
|
5356
|
+
const displayName = getFrameworkDisplayName(framework);
|
|
5357
|
+
switch (framework) {
|
|
5358
|
+
case "fastapi":
|
|
5359
|
+
return `${found} but ${subject} ${verb} a top-level "app" FastAPI instance.`;
|
|
5360
|
+
case "flask":
|
|
5361
|
+
return `${found} but ${subject} ${verb} a top-level "app" Flask instance.`;
|
|
5362
|
+
case "django":
|
|
5363
|
+
return `${found} but ${subject} ${verb} a top-level "application" ASGI or WSGI instance.`;
|
|
5364
|
+
case "fasthtml":
|
|
5365
|
+
return `${found} but ${subject} ${verb} a top-level "app" ${displayName} instance.`;
|
|
5366
|
+
default:
|
|
5367
|
+
return entrypoints.length === 1 ? `Found ${fileList} but it does not export a top-level "app", "application", or "handler" variable.` : `Found ${fileList} but none export a top-level "app", "application", or "handler" variable.`;
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5298
5370
|
async function checkEntrypoint(workPath, relPath) {
|
|
5299
5371
|
const absPath = (0, import_path8.join)(workPath, relPath);
|
|
5300
5372
|
if (!await fileExists(absPath))
|
|
@@ -5302,6 +5374,28 @@ async function checkEntrypoint(workPath, relPath) {
|
|
|
5302
5374
|
const content = await import_fs8.default.promises.readFile(absPath, "utf-8");
|
|
5303
5375
|
return (0, import_python_analysis5.findAppOrHandler)(content);
|
|
5304
5376
|
}
|
|
5377
|
+
async function findOtherPyFiles(workPath, dirs, candidateSet) {
|
|
5378
|
+
const results = [];
|
|
5379
|
+
for (const dir of dirs) {
|
|
5380
|
+
const dirPath = (0, import_path8.join)(workPath, dir);
|
|
5381
|
+
try {
|
|
5382
|
+
const entries = await import_fs8.default.promises.readdir(dirPath);
|
|
5383
|
+
for (const entry of entries) {
|
|
5384
|
+
if (!entry.endsWith(".py"))
|
|
5385
|
+
continue;
|
|
5386
|
+
const relPath = dir ? import_path8.posix.join(dir, entry) : entry;
|
|
5387
|
+
if (candidateSet.has(relPath))
|
|
5388
|
+
continue;
|
|
5389
|
+
const stat = await import_fs8.default.promises.stat((0, import_path8.join)(dirPath, entry));
|
|
5390
|
+
if (stat.isFile()) {
|
|
5391
|
+
results.push(relPath);
|
|
5392
|
+
}
|
|
5393
|
+
}
|
|
5394
|
+
} catch {
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
return results;
|
|
5398
|
+
}
|
|
5305
5399
|
async function resolveModuleAttrEntrypoint(workPath, value) {
|
|
5306
5400
|
const match = value.match(/([A-Za-z_][\w.]*)\s*:\s*([A-Za-z_][\w]*)/);
|
|
5307
5401
|
if (!match)
|
|
@@ -5326,25 +5420,46 @@ async function getVercelToolsEntrypoint(workPath) {
|
|
|
5326
5420
|
return null;
|
|
5327
5421
|
return resolveModuleAttrEntrypoint(workPath, vercelEntrypoint);
|
|
5328
5422
|
}
|
|
5329
|
-
async function
|
|
5423
|
+
async function getPyprojectEntrypointWithDiagnostics(workPath) {
|
|
5330
5424
|
const pyprojectData = await (0, import_build_utils10.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
|
|
5331
5425
|
if (!pyprojectData)
|
|
5332
|
-
return null;
|
|
5426
|
+
return { entrypoint: null };
|
|
5333
5427
|
const scripts = pyprojectData.project?.scripts;
|
|
5334
5428
|
const appScript = scripts?.app;
|
|
5335
5429
|
if (typeof appScript !== "string")
|
|
5336
|
-
return null;
|
|
5337
|
-
|
|
5430
|
+
return { entrypoint: null };
|
|
5431
|
+
const match = appScript.match(/([A-Za-z_][\w.]*)\s*:\s*([A-Za-z_][\w]*)/);
|
|
5432
|
+
if (!match)
|
|
5433
|
+
return { entrypoint: null, appScript };
|
|
5434
|
+
const modulePath = match[1];
|
|
5435
|
+
const variableName = match[2];
|
|
5436
|
+
const relPath = modulePath.replace(/\./g, "/");
|
|
5437
|
+
const candidates = [`${relPath}.py`, `${relPath}/__init__.py`];
|
|
5438
|
+
for (const candidate of candidates) {
|
|
5439
|
+
if (await fileExists((0, import_path8.join)(workPath, candidate))) {
|
|
5440
|
+
return { entrypoint: { entrypoint: candidate, variableName } };
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
return { entrypoint: null, appScript, checkedPaths: candidates };
|
|
5338
5444
|
}
|
|
5339
5445
|
async function findValidEntrypoint(workPath, candidates) {
|
|
5446
|
+
const existingWithoutEntrypoint = [];
|
|
5340
5447
|
for (const candidate of candidates) {
|
|
5341
|
-
const
|
|
5448
|
+
const absPath = (0, import_path8.join)(workPath, candidate);
|
|
5449
|
+
if (!await fileExists(absPath))
|
|
5450
|
+
continue;
|
|
5451
|
+
const content = await import_fs8.default.promises.readFile(absPath, "utf-8");
|
|
5452
|
+
const varName = await (0, import_python_analysis5.findAppOrHandler)(content);
|
|
5342
5453
|
if (varName) {
|
|
5343
5454
|
(0, import_build_utils9.debug)(`Detected Python entrypoint: ${candidate} (variable: ${varName})`);
|
|
5344
|
-
return {
|
|
5455
|
+
return {
|
|
5456
|
+
entrypoint: { entrypoint: candidate, variableName: varName },
|
|
5457
|
+
existingWithoutEntrypoint
|
|
5458
|
+
};
|
|
5345
5459
|
}
|
|
5460
|
+
existingWithoutEntrypoint.push(candidate);
|
|
5346
5461
|
}
|
|
5347
|
-
return null;
|
|
5462
|
+
return { entrypoint: null, existingWithoutEntrypoint };
|
|
5348
5463
|
}
|
|
5349
5464
|
async function checkDjangoManage(workPath) {
|
|
5350
5465
|
const managePath = (0, import_path8.join)(workPath, "manage.py");
|
|
@@ -5368,28 +5483,56 @@ async function getSubdirectories(workPath) {
|
|
|
5368
5483
|
return [];
|
|
5369
5484
|
}
|
|
5370
5485
|
}
|
|
5371
|
-
function
|
|
5372
|
-
const
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5486
|
+
function makeDiagnosticError(framework, diagnostics2) {
|
|
5487
|
+
const code = `${framework.toUpperCase()}_ENTRYPOINT_NOT_FOUND`;
|
|
5488
|
+
const link = getFrameworkEntrypointDocsUrl(framework);
|
|
5489
|
+
const action = "Learn More";
|
|
5490
|
+
const displayName = getFrameworkDisplayName(framework);
|
|
5491
|
+
let message;
|
|
5492
|
+
if (diagnostics2.existingWithoutEntrypoint.length > 0) {
|
|
5493
|
+
message = getMissingExportMessage(
|
|
5494
|
+
framework,
|
|
5495
|
+
diagnostics2.existingWithoutEntrypoint
|
|
5496
|
+
);
|
|
5497
|
+
} else if (diagnostics2.pyprojectAppScript && diagnostics2.pyprojectCheckedPaths?.length) {
|
|
5498
|
+
const checked = diagnostics2.pyprojectCheckedPaths.join(" or ");
|
|
5499
|
+
message = `pyproject.toml [project.scripts] defines app = "${diagnostics2.pyprojectAppScript}" but ${checked} was not found.`;
|
|
5500
|
+
} else if (diagnostics2.otherPyFiles.length > 0) {
|
|
5501
|
+
const fileList = diagnostics2.otherPyFiles.slice(0, 5).join(", ");
|
|
5502
|
+
message = `No ${displayName} entrypoint found in standard locations. Found Python files: ${fileList}. Rename one to app.py or set "tool.vercel.entrypoint" in pyproject.toml.`;
|
|
5503
|
+
} else {
|
|
5504
|
+
const searchedList = PYTHON_CANDIDATE_ENTRYPOINTS.join(", ");
|
|
5505
|
+
message = `No ${displayName} entrypoint found. Set "tool.vercel.entrypoint" in pyproject.toml or define an entrypoint in one of: ${searchedList}.`;
|
|
5506
|
+
}
|
|
5507
|
+
return new import_build_utils8.NowBuildError({ code, message, link, action });
|
|
5379
5508
|
}
|
|
5380
5509
|
async function detectGenericPythonEntrypoint(workPath) {
|
|
5381
5510
|
try {
|
|
5382
|
-
const
|
|
5511
|
+
const result = await findValidEntrypoint(
|
|
5383
5512
|
workPath,
|
|
5384
5513
|
PYTHON_CANDIDATE_ENTRYPOINTS
|
|
5385
5514
|
);
|
|
5386
|
-
return
|
|
5515
|
+
return {
|
|
5516
|
+
detected: result.entrypoint ? { entrypoint: result.entrypoint } : null,
|
|
5517
|
+
findDiagnostics: result
|
|
5518
|
+
};
|
|
5387
5519
|
} catch {
|
|
5388
5520
|
(0, import_build_utils9.debug)("Failed to discover Python entrypoint");
|
|
5389
|
-
return
|
|
5521
|
+
return {
|
|
5522
|
+
detected: null,
|
|
5523
|
+
findDiagnostics: { entrypoint: null, existingWithoutEntrypoint: [] }
|
|
5524
|
+
};
|
|
5390
5525
|
}
|
|
5391
5526
|
}
|
|
5392
5527
|
async function detectDjangoPythonEntrypoint(workPath) {
|
|
5528
|
+
const emptyResult = {
|
|
5529
|
+
detected: null,
|
|
5530
|
+
findDiagnostics: {
|
|
5531
|
+
entrypoint: null,
|
|
5532
|
+
existingWithoutEntrypoint: []
|
|
5533
|
+
},
|
|
5534
|
+
dirs: PYTHON_ENTRYPOINT_DIRS
|
|
5535
|
+
};
|
|
5393
5536
|
try {
|
|
5394
5537
|
const subdirs = await getSubdirectories(workPath);
|
|
5395
5538
|
const rootDirs = ["", ...subdirs];
|
|
@@ -5397,21 +5540,40 @@ async function detectDjangoPythonEntrypoint(workPath) {
|
|
|
5397
5540
|
const currPath = (0, import_path8.join)(workPath, rootDir);
|
|
5398
5541
|
const isDjango = await checkDjangoManage(currPath);
|
|
5399
5542
|
if (isDjango) {
|
|
5400
|
-
return {
|
|
5543
|
+
return {
|
|
5544
|
+
detected: { baseDir: rootDir },
|
|
5545
|
+
findDiagnostics: { entrypoint: null, existingWithoutEntrypoint: [] },
|
|
5546
|
+
dirs: rootDirs
|
|
5547
|
+
};
|
|
5401
5548
|
}
|
|
5402
5549
|
}
|
|
5403
5550
|
const candidates = getCandidateEntrypointsInDirs(rootDirs);
|
|
5404
|
-
const
|
|
5405
|
-
return
|
|
5551
|
+
const result = await findValidEntrypoint(workPath, candidates);
|
|
5552
|
+
return {
|
|
5553
|
+
detected: result.entrypoint ? { entrypoint: result.entrypoint } : null,
|
|
5554
|
+
findDiagnostics: result,
|
|
5555
|
+
dirs: rootDirs
|
|
5556
|
+
};
|
|
5406
5557
|
} catch {
|
|
5407
5558
|
(0, import_build_utils9.debug)("Failed to discover Django Python entrypoint");
|
|
5408
|
-
return
|
|
5559
|
+
return emptyResult;
|
|
5409
5560
|
}
|
|
5410
5561
|
}
|
|
5411
5562
|
async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint, service) {
|
|
5412
5563
|
if (configuredEntrypoint) {
|
|
5413
5564
|
const { filePath: configEntryFile, varName: configEntryVar } = configuredEntrypoint;
|
|
5414
5565
|
const entrypoint = configEntryFile.endsWith(".py") ? configEntryFile : `${configEntryFile}.py`;
|
|
5566
|
+
const entrypointExists = await fileExists((0, import_path8.join)(workPath, entrypoint));
|
|
5567
|
+
if (!entrypointExists) {
|
|
5568
|
+
return {
|
|
5569
|
+
error: new import_build_utils8.NowBuildError({
|
|
5570
|
+
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
5571
|
+
message: `Configured Python entrypoint "${entrypoint}" was not found.`,
|
|
5572
|
+
link: PYTHON_ENTRYPOINT_DOCS_URL,
|
|
5573
|
+
action: "Learn More"
|
|
5574
|
+
})
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5415
5577
|
let varName = configEntryVar ?? await checkEntrypoint(workPath, entrypoint);
|
|
5416
5578
|
if (!varName) {
|
|
5417
5579
|
const needsDynamicApp = !!service && ((0, import_build_utils8.isScheduleTriggeredService)(service) || (0, import_build_utils8.isQueueTriggeredService)(service));
|
|
@@ -5427,7 +5589,7 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5427
5589
|
error: new import_build_utils8.NowBuildError({
|
|
5428
5590
|
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
5429
5591
|
message: `Could not find a top-level "app", "application", or "handler" in "${entrypoint}".`,
|
|
5430
|
-
link:
|
|
5592
|
+
link: PYTHON_ENTRYPOINT_DOCS_URL,
|
|
5431
5593
|
action: "Learn More"
|
|
5432
5594
|
})
|
|
5433
5595
|
};
|
|
@@ -5439,11 +5601,57 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
|
|
|
5439
5601
|
const vercelEntry = await getVercelToolsEntrypoint(workPath);
|
|
5440
5602
|
if (vercelEntry)
|
|
5441
5603
|
return { entrypoint: vercelEntry };
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5604
|
+
let findDiagnostics;
|
|
5605
|
+
let searchDirs;
|
|
5606
|
+
if (framework === "django") {
|
|
5607
|
+
const djangoResult = await detectDjangoPythonEntrypoint(workPath);
|
|
5608
|
+
findDiagnostics = djangoResult.findDiagnostics;
|
|
5609
|
+
searchDirs = djangoResult.dirs;
|
|
5610
|
+
if (djangoResult.detected) {
|
|
5611
|
+
if (djangoResult.detected.entrypoint)
|
|
5612
|
+
return djangoResult.detected;
|
|
5613
|
+
const candidateSet2 = new Set(getCandidateEntrypointsInDirs(searchDirs));
|
|
5614
|
+
const otherPyFiles2 = await findOtherPyFiles(
|
|
5615
|
+
workPath,
|
|
5616
|
+
searchDirs,
|
|
5617
|
+
candidateSet2
|
|
5618
|
+
);
|
|
5619
|
+
const pyprojectResult2 = await getPyprojectEntrypointWithDiagnostics(workPath);
|
|
5620
|
+
const diagnostics3 = {
|
|
5621
|
+
existingWithoutEntrypoint: findDiagnostics.existingWithoutEntrypoint,
|
|
5622
|
+
otherPyFiles: otherPyFiles2,
|
|
5623
|
+
pyprojectAppScript: pyprojectResult2.appScript,
|
|
5624
|
+
pyprojectCheckedPaths: pyprojectResult2.checkedPaths
|
|
5625
|
+
};
|
|
5626
|
+
return {
|
|
5627
|
+
baseDir: djangoResult.detected.baseDir,
|
|
5628
|
+
error: makeDiagnosticError("django", diagnostics3)
|
|
5629
|
+
};
|
|
5630
|
+
}
|
|
5631
|
+
} else {
|
|
5632
|
+
const genericResult = await detectGenericPythonEntrypoint(workPath);
|
|
5633
|
+
findDiagnostics = genericResult.findDiagnostics;
|
|
5634
|
+
searchDirs = PYTHON_ENTRYPOINT_DIRS;
|
|
5635
|
+
if (genericResult.detected)
|
|
5636
|
+
return genericResult.detected;
|
|
5637
|
+
}
|
|
5638
|
+
const pyprojectResult = await getPyprojectEntrypointWithDiagnostics(workPath);
|
|
5639
|
+
if (pyprojectResult.entrypoint) {
|
|
5640
|
+
return { entrypoint: pyprojectResult.entrypoint };
|
|
5641
|
+
}
|
|
5642
|
+
const candidateSet = new Set(getCandidateEntrypointsInDirs(searchDirs));
|
|
5643
|
+
const otherPyFiles = await findOtherPyFiles(
|
|
5644
|
+
workPath,
|
|
5645
|
+
searchDirs,
|
|
5646
|
+
candidateSet
|
|
5647
|
+
);
|
|
5648
|
+
const diagnostics2 = {
|
|
5649
|
+
existingWithoutEntrypoint: findDiagnostics.existingWithoutEntrypoint,
|
|
5650
|
+
otherPyFiles,
|
|
5651
|
+
pyprojectAppScript: pyprojectResult.appScript,
|
|
5652
|
+
pyprojectCheckedPaths: pyprojectResult.checkedPaths
|
|
5653
|
+
};
|
|
5654
|
+
return { error: makeDiagnosticError(framework, diagnostics2) };
|
|
5447
5655
|
}
|
|
5448
5656
|
|
|
5449
5657
|
// src/start-dev-server.ts
|
|
@@ -6794,6 +7002,7 @@ async function runDjangoCollectStatic(venvPath, workPath, env, outputStaticDir,
|
|
|
6794
7002
|
// src/index.ts
|
|
6795
7003
|
var import_python_analysis9 = require("@vercel/python-analysis");
|
|
6796
7004
|
var writeFile = import_fs13.default.promises.writeFile;
|
|
7005
|
+
var PYTHON_ENTRYPOINT_DOCS_URL2 = "https://vercel.com/docs/functions/runtimes/python#python-entrypoints";
|
|
6797
7006
|
var version = -1;
|
|
6798
7007
|
async function runFrameworkHook(framework, ctx) {
|
|
6799
7008
|
const hook = framework ? frameworkHooks[framework] : void 0;
|
|
@@ -6881,6 +7090,15 @@ async function downloadFilesInWorkPath({
|
|
|
6881
7090
|
(0, import_build_utils16.debug)("Downloading user files...");
|
|
6882
7091
|
let downloadedFiles = await (0, import_build_utils16.download)(files, workPath, meta);
|
|
6883
7092
|
if (meta.isDev && entrypoint) {
|
|
7093
|
+
const normalizedEntrypoint = entrypoint.endsWith(".py") ? entrypoint : `${entrypoint}.py`;
|
|
7094
|
+
if (!hasProp(downloadedFiles, entrypoint) && !hasProp(downloadedFiles, normalizedEntrypoint)) {
|
|
7095
|
+
throw new import_build_utils16.NowBuildError({
|
|
7096
|
+
code: "PYTHON_ENTRYPOINT_NOT_FOUND",
|
|
7097
|
+
message: `Configured Python entrypoint "${normalizedEntrypoint}" was not found.`,
|
|
7098
|
+
link: PYTHON_ENTRYPOINT_DOCS_URL2,
|
|
7099
|
+
action: "Learn More"
|
|
7100
|
+
});
|
|
7101
|
+
}
|
|
6884
7102
|
const { devCacheDir = (0, import_path13.join)(workPath, ".now", "cache") } = meta;
|
|
6885
7103
|
const cacheKey = (0, import_path13.basename)(entrypoint).replace(/\./g, "_");
|
|
6886
7104
|
const destCache = (0, import_path13.join)(devCacheDir, cacheKey);
|
|
@@ -6986,6 +7204,7 @@ var build = async ({
|
|
|
6986
7204
|
const venvPath = service?.name ? (0, import_path13.join)(workPath, ".vercel", "python", "services", service.name, ".venv") : (0, import_path13.join)(workPath, ".vercel", "python", ".venv");
|
|
6987
7205
|
const hasCachedVenv = import_fs13.default.existsSync((0, import_path13.join)(venvPath, "pyvenv.cfg"));
|
|
6988
7206
|
const hasCachedUv = import_fs13.default.existsSync(uvCacheDir);
|
|
7207
|
+
const restoredCache = hasCachedVenv && hasCachedUv ? "both" : hasCachedVenv ? "venv" : hasCachedUv ? "uv" : "none";
|
|
6989
7208
|
if (hasCachedVenv || hasCachedUv) {
|
|
6990
7209
|
(0, import_build_utils16.debug)(
|
|
6991
7210
|
`Build cache detected: venv=${hasCachedVenv}, uv-cache=${hasCachedUv}`
|
|
@@ -7032,7 +7251,9 @@ var build = async ({
|
|
|
7032
7251
|
let uvProjectDir = null;
|
|
7033
7252
|
let projectName;
|
|
7034
7253
|
await builderSpan.child(import_build_utils16.BUILDER_INSTALLER_STEP, {
|
|
7035
|
-
installCommand: projectInstallCommand || void 0
|
|
7254
|
+
installCommand: projectInstallCommand || void 0,
|
|
7255
|
+
runtime: "python",
|
|
7256
|
+
"python.cache.restored": restoredCache
|
|
7036
7257
|
}).trace(async () => {
|
|
7037
7258
|
if (projectInstallCommand) {
|
|
7038
7259
|
await import_fs13.default.promises.rm(venvPath, { recursive: true, force: true });
|
|
@@ -7343,9 +7564,6 @@ var prepareCache = async ({
|
|
|
7343
7564
|
repoRootPath,
|
|
7344
7565
|
workPath
|
|
7345
7566
|
}) => {
|
|
7346
|
-
if (process.env.VERCEL_PYTHON_PREPARE_CACHE !== "1") {
|
|
7347
|
-
return {};
|
|
7348
|
-
}
|
|
7349
7567
|
const root = repoRootPath || workPath;
|
|
7350
7568
|
const ignore = ["**/*.pyc", "**/__pycache__/**"];
|
|
7351
7569
|
const uvCacheDir = getUvCacheDir(workPath);
|