@vercel/python 6.55.3 → 6.56.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 +313 -70
- package/package.json +5 -8
package/dist/index.js
CHANGED
|
@@ -5032,12 +5032,29 @@ var import_fs20 = __toESM(require("fs"));
|
|
|
5032
5032
|
var import_path21 = require("path");
|
|
5033
5033
|
|
|
5034
5034
|
// src/package-versions.ts
|
|
5035
|
-
var VERCEL_RUNTIME_VERSION = "0.
|
|
5035
|
+
var VERCEL_RUNTIME_VERSION = "0.19.0";
|
|
5036
5036
|
var VERCEL_WORKERS_VERSION = "0.0.25";
|
|
5037
5037
|
|
|
5038
5038
|
// src/conditional-vendoring.ts
|
|
5039
5039
|
var import_python_analysis = require("@vercel/python-analysis");
|
|
5040
5040
|
var UPSTREAM_DEPENDENCY_ADAPTERS = /* @__PURE__ */ new Map([
|
|
5041
|
+
[
|
|
5042
|
+
"apscheduler",
|
|
5043
|
+
{
|
|
5044
|
+
bundled: "vercel-apscheduler-bundle",
|
|
5045
|
+
unbundled: "vercel-apscheduler",
|
|
5046
|
+
envOverride: "VERCEL_PYTHON_APSCHEDULER_DEPENDENCY",
|
|
5047
|
+
preferUnbundledWhenPresent: ["vercel-queue"],
|
|
5048
|
+
integration: {
|
|
5049
|
+
module: "vercel.integrations.apscheduler",
|
|
5050
|
+
installer: "install_vercel_apscheduler_integration",
|
|
5051
|
+
// APScheduler jobs are declared while the subscriber module imports.
|
|
5052
|
+
// Install first so the adapter can capture those definitions.
|
|
5053
|
+
installBeforeImport: true,
|
|
5054
|
+
subscriberProbe: "is_scheduler_subscriber"
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
],
|
|
5041
5058
|
[
|
|
5042
5059
|
"celery",
|
|
5043
5060
|
{
|
|
@@ -5069,31 +5086,26 @@ var UPSTREAM_DEPENDENCY_ADAPTERS = /* @__PURE__ */ new Map([
|
|
|
5069
5086
|
}
|
|
5070
5087
|
]
|
|
5071
5088
|
]);
|
|
5072
|
-
async function
|
|
5073
|
-
pythonPackage
|
|
5089
|
+
async function getQueueAdapterBootstrap({
|
|
5090
|
+
pythonPackage,
|
|
5091
|
+
env,
|
|
5092
|
+
legacyWorkersProject,
|
|
5093
|
+
hasDeclaredSubscribers
|
|
5074
5094
|
}) {
|
|
5075
|
-
const dependencies = await getDirectDependencyNames(pythonPackage);
|
|
5076
|
-
if (!dependencies)
|
|
5077
|
-
return [];
|
|
5078
5095
|
const integrations = [];
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5096
|
+
const injectedPackages = [];
|
|
5097
|
+
const bootstrap = { integrations, injectedPackages };
|
|
5098
|
+
if (legacyWorkersProject || !hasDeclaredSubscribers) {
|
|
5099
|
+
return bootstrap;
|
|
5083
5100
|
}
|
|
5084
|
-
return integrations;
|
|
5085
|
-
}
|
|
5086
|
-
async function getConditionalInjectedPackages({
|
|
5087
|
-
pythonPackage,
|
|
5088
|
-
env
|
|
5089
|
-
}) {
|
|
5090
5101
|
const dependencies = await getDirectDependencyNames(pythonPackage);
|
|
5091
|
-
if (!dependencies)
|
|
5092
|
-
return
|
|
5093
|
-
|
|
5102
|
+
if (!dependencies) {
|
|
5103
|
+
return bootstrap;
|
|
5104
|
+
}
|
|
5094
5105
|
for (const [upstream, adapter] of UPSTREAM_DEPENDENCY_ADAPTERS) {
|
|
5095
5106
|
if (!dependencies.has(upstream))
|
|
5096
5107
|
continue;
|
|
5108
|
+
integrations.push(adapter.integration);
|
|
5097
5109
|
if (dependencies.has(adapter.bundled) || dependencies.has(adapter.unbundled)) {
|
|
5098
5110
|
continue;
|
|
5099
5111
|
}
|
|
@@ -5107,7 +5119,7 @@ async function getConditionalInjectedPackages({
|
|
|
5107
5119
|
allowLocalSource: false
|
|
5108
5120
|
});
|
|
5109
5121
|
}
|
|
5110
|
-
return
|
|
5122
|
+
return bootstrap;
|
|
5111
5123
|
}
|
|
5112
5124
|
async function getDirectDependencyNames(pythonPackage) {
|
|
5113
5125
|
const dependencies = pythonPackage?.manifest?.data?.project?.dependencies;
|
|
@@ -7979,6 +7991,7 @@ var import_build_utils14 = require("@vercel/build-utils");
|
|
|
7979
7991
|
var import_path10 = require("path");
|
|
7980
7992
|
var import_fs10 = __toESM(require("fs"));
|
|
7981
7993
|
var MODULE_ATTR_RE = /^([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*):([A-Za-z_][\w]*)$/;
|
|
7994
|
+
var MODULE_RE = /^[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*$/;
|
|
7982
7995
|
function parseModuleEntrypoint(value) {
|
|
7983
7996
|
const match = MODULE_ATTR_RE.exec(value);
|
|
7984
7997
|
if (!match) {
|
|
@@ -7990,11 +8003,21 @@ function parseModuleEntrypoint(value) {
|
|
|
7990
8003
|
filePath: `${match[1].replace(/\./g, "/")}.py`
|
|
7991
8004
|
};
|
|
7992
8005
|
}
|
|
8006
|
+
function parseBareModuleEntrypoint(value) {
|
|
8007
|
+
if (!MODULE_RE.test(value)) {
|
|
8008
|
+
return null;
|
|
8009
|
+
}
|
|
8010
|
+
return {
|
|
8011
|
+
moduleName: value,
|
|
8012
|
+
filePath: `${value.replace(/\./g, "/")}.py`
|
|
8013
|
+
};
|
|
8014
|
+
}
|
|
7993
8015
|
function getModuleEntrypointName({
|
|
7994
8016
|
moduleName,
|
|
7995
8017
|
variableName
|
|
7996
8018
|
}) {
|
|
7997
|
-
|
|
8019
|
+
const base = moduleName.replace(/\./g, "-");
|
|
8020
|
+
return variableName === void 0 ? base : `${base}_${variableName}`;
|
|
7998
8021
|
}
|
|
7999
8022
|
function safePathSegment(value) {
|
|
8000
8023
|
return [...value].map((char) => {
|
|
@@ -8108,6 +8131,7 @@ function workflowError(message) {
|
|
|
8108
8131
|
|
|
8109
8132
|
// src/subscribers.ts
|
|
8110
8133
|
var SUBSCRIBER_OUTPUT_DIR = "_py_subscribers";
|
|
8134
|
+
var SUBSCRIBER_ID_ENV = "VERCEL_PYTHON_SUBSCRIBER_ID";
|
|
8111
8135
|
var TRIGGER_NUMBER_FIELDS = [
|
|
8112
8136
|
{
|
|
8113
8137
|
field: "max_attempts",
|
|
@@ -8166,6 +8190,13 @@ var LEGACY_SUBSCRIBER_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
|
8166
8190
|
"topics",
|
|
8167
8191
|
...LEGACY_TRIGGER_NUMBER_FIELDS.map(({ field }) => field)
|
|
8168
8192
|
]);
|
|
8193
|
+
var APSCHEDULER_PREVIEW_FIELD_NAMES = /* @__PURE__ */ new Set(["enabled", "idle_timeout"]);
|
|
8194
|
+
var APSCHEDULER_DURATION_UNITS = {
|
|
8195
|
+
s: 1,
|
|
8196
|
+
m: 60,
|
|
8197
|
+
h: 60 * 60,
|
|
8198
|
+
d: 24 * 60 * 60
|
|
8199
|
+
};
|
|
8169
8200
|
function getSubscriberOutputPath(subscriberName) {
|
|
8170
8201
|
return `${SUBSCRIBER_OUTPUT_DIR}/${safePathSegment(subscriberName)}`;
|
|
8171
8202
|
}
|
|
@@ -8178,6 +8209,15 @@ function getGeneratedQueueHandlerPath(outputPath) {
|
|
|
8178
8209
|
function generatedPythonPathToModule(filePath) {
|
|
8179
8210
|
return filePath.replace(/\.py$/, "").split(/[\\/]+/).join(".");
|
|
8180
8211
|
}
|
|
8212
|
+
async function hasPyprojectSubscribers(workPath) {
|
|
8213
|
+
const pyprojectPath = (0, import_path12.join)(workPath, "pyproject.toml");
|
|
8214
|
+
if (!import_fs12.default.existsSync(pyprojectPath)) {
|
|
8215
|
+
return false;
|
|
8216
|
+
}
|
|
8217
|
+
const pyproject = await (0, import_build_utils14.readConfigFile)(pyprojectPath);
|
|
8218
|
+
const subscribers = pyproject?.tool?.vercel?.subscribers;
|
|
8219
|
+
return Array.isArray(subscribers) && subscribers.length > 0;
|
|
8220
|
+
}
|
|
8181
8221
|
async function getPyprojectSubscribers(workPath, { legacySchema = false } = {}) {
|
|
8182
8222
|
const pyprojectPath = (0, import_path12.join)(workPath, "pyproject.toml");
|
|
8183
8223
|
if (!import_fs12.default.existsSync(pyprojectPath)) {
|
|
@@ -8207,6 +8247,63 @@ async function getPyprojectSubscribers(workPath, { legacySchema = false } = {})
|
|
|
8207
8247
|
}
|
|
8208
8248
|
return parsedSubscribers;
|
|
8209
8249
|
}
|
|
8250
|
+
async function getApschedulerPreviewConfig(workPath) {
|
|
8251
|
+
const pyprojectPath = (0, import_path12.join)(workPath, "pyproject.toml");
|
|
8252
|
+
if (!import_fs12.default.existsSync(pyprojectPath)) {
|
|
8253
|
+
return void 0;
|
|
8254
|
+
}
|
|
8255
|
+
const pyproject = await (0, import_build_utils14.readConfigFile)(pyprojectPath);
|
|
8256
|
+
const apscheduler = pyproject?.tool?.vercel?.apscheduler;
|
|
8257
|
+
if (apscheduler === void 0) {
|
|
8258
|
+
return void 0;
|
|
8259
|
+
}
|
|
8260
|
+
if (!apscheduler || typeof apscheduler !== "object" || Array.isArray(apscheduler)) {
|
|
8261
|
+
throw apschedulerError('"tool.vercel.apscheduler" must be a table');
|
|
8262
|
+
}
|
|
8263
|
+
const previews = apscheduler.previews;
|
|
8264
|
+
if (previews === void 0) {
|
|
8265
|
+
return void 0;
|
|
8266
|
+
}
|
|
8267
|
+
if (!previews || typeof previews !== "object" || Array.isArray(previews)) {
|
|
8268
|
+
throw apschedulerError(
|
|
8269
|
+
'"tool.vercel.apscheduler.previews" must be a table'
|
|
8270
|
+
);
|
|
8271
|
+
}
|
|
8272
|
+
const rawPreviews = previews;
|
|
8273
|
+
for (const key of Object.keys(rawPreviews)) {
|
|
8274
|
+
if (!APSCHEDULER_PREVIEW_FIELD_NAMES.has(key)) {
|
|
8275
|
+
throw apschedulerError(
|
|
8276
|
+
`"tool.vercel.apscheduler.previews" has unrecognized field "${key}"`
|
|
8277
|
+
);
|
|
8278
|
+
}
|
|
8279
|
+
}
|
|
8280
|
+
if (rawPreviews.enabled !== void 0 && typeof rawPreviews.enabled !== "boolean") {
|
|
8281
|
+
throw apschedulerError(
|
|
8282
|
+
'"tool.vercel.apscheduler.previews.enabled" must be a boolean'
|
|
8283
|
+
);
|
|
8284
|
+
}
|
|
8285
|
+
if (rawPreviews.enabled !== true) {
|
|
8286
|
+
return void 0;
|
|
8287
|
+
}
|
|
8288
|
+
if (typeof rawPreviews.idle_timeout !== "string") {
|
|
8289
|
+
throw apschedulerError(
|
|
8290
|
+
'"tool.vercel.apscheduler.previews.idle_timeout" must be a duration such as "30m"'
|
|
8291
|
+
);
|
|
8292
|
+
}
|
|
8293
|
+
const match = /^([1-9]\d*)([smhd])$/.exec(rawPreviews.idle_timeout);
|
|
8294
|
+
if (!match) {
|
|
8295
|
+
throw apschedulerError(
|
|
8296
|
+
'"tool.vercel.apscheduler.previews.idle_timeout" must be a positive duration using s, m, h, or d (for example "30m")'
|
|
8297
|
+
);
|
|
8298
|
+
}
|
|
8299
|
+
const idleTimeoutSeconds = Number(match[1]) * APSCHEDULER_DURATION_UNITS[match[2]];
|
|
8300
|
+
if (!Number.isSafeInteger(idleTimeoutSeconds)) {
|
|
8301
|
+
throw apschedulerError(
|
|
8302
|
+
'"tool.vercel.apscheduler.previews.idle_timeout" is too large'
|
|
8303
|
+
);
|
|
8304
|
+
}
|
|
8305
|
+
return { idleTimeoutSeconds };
|
|
8306
|
+
}
|
|
8210
8307
|
async function resolveQueueSubscribers({
|
|
8211
8308
|
declarations,
|
|
8212
8309
|
uv,
|
|
@@ -8228,10 +8325,12 @@ async function resolveQueueSubscribers({
|
|
|
8228
8325
|
const hint = kind === "workflow" ? "; ensure the entrypoint instantiates vercel.workflow.Workflows" : "";
|
|
8229
8326
|
const unmatchedTopicPatterns = getUnmatchedQueueTopicPatterns(
|
|
8230
8327
|
declaration,
|
|
8231
|
-
introspected
|
|
8328
|
+
introspected.subscriptions
|
|
8232
8329
|
);
|
|
8233
8330
|
if (unmatchedTopicPatterns.length > 0) {
|
|
8234
|
-
const introspectedTopics = introspected.map(
|
|
8331
|
+
const introspectedTopics = introspected.subscriptions.map(
|
|
8332
|
+
({ topic }) => topic
|
|
8333
|
+
);
|
|
8235
8334
|
throw subscriberError(
|
|
8236
8335
|
`${kind} "${declaration.name}" declared topics [${unmatchedTopicPatterns.join(
|
|
8237
8336
|
", "
|
|
@@ -8240,9 +8339,9 @@ async function resolveQueueSubscribers({
|
|
|
8240
8339
|
)}]${hint}`
|
|
8241
8340
|
);
|
|
8242
8341
|
}
|
|
8243
|
-
const subscriptions = kind === "workflow" ? introspected.filter(
|
|
8342
|
+
const subscriptions = kind === "workflow" ? introspected.subscriptions.filter(
|
|
8244
8343
|
(subscription) => isWorkflowQueueTopic(subscription.topic)
|
|
8245
|
-
) : filterQueueSubscriptions(declaration, introspected);
|
|
8344
|
+
) : filterQueueSubscriptions(declaration, introspected.subscriptions);
|
|
8246
8345
|
if (subscriptions.length === 0) {
|
|
8247
8346
|
if (kind === "workflow") {
|
|
8248
8347
|
throw subscriberError(
|
|
@@ -8254,7 +8353,7 @@ async function resolveQueueSubscribers({
|
|
|
8254
8353
|
`${kind} "${declaration.name}" declared topics [${declared}] but no introspected queue subscriptions matched${hint}`
|
|
8255
8354
|
);
|
|
8256
8355
|
}
|
|
8257
|
-
result.push({ ...declaration, subscriptions });
|
|
8356
|
+
result.push({ ...declaration, subscriptions, owners: introspected.owners });
|
|
8258
8357
|
}
|
|
8259
8358
|
return result;
|
|
8260
8359
|
}
|
|
@@ -8307,8 +8406,10 @@ function createIntegrationInstallLines(integrations, { serving, beforeImport })
|
|
|
8307
8406
|
function createQueueHandlerModule(declaration, integrations) {
|
|
8308
8407
|
return [
|
|
8309
8408
|
"import importlib",
|
|
8409
|
+
"import os",
|
|
8310
8410
|
"import vercel.queue",
|
|
8311
8411
|
"",
|
|
8412
|
+
`os.environ[${JSON.stringify(SUBSCRIBER_ID_ENV)}] = ${JSON.stringify(declaration.name)}`,
|
|
8312
8413
|
...createIntegrationInstallLines(integrations, {
|
|
8313
8414
|
serving: true,
|
|
8314
8415
|
beforeImport: true
|
|
@@ -8322,14 +8423,22 @@ function createQueueHandlerModule(declaration, integrations) {
|
|
|
8322
8423
|
""
|
|
8323
8424
|
].join("\n");
|
|
8324
8425
|
}
|
|
8325
|
-
async function parseSubscriberEntrypoint(workPath, label, entrypointValue) {
|
|
8426
|
+
async function parseSubscriberEntrypoint(workPath, label, entrypointValue, { requireVariable }) {
|
|
8326
8427
|
if (typeof entrypointValue !== "string") {
|
|
8327
8428
|
throw subscriberError(`${label} must define string field "entrypoint"`);
|
|
8328
8429
|
}
|
|
8329
|
-
|
|
8430
|
+
if (!requireVariable && /\.py$/i.test(entrypointValue)) {
|
|
8431
|
+
const suggestion = parseBareModuleEntrypoint(
|
|
8432
|
+
entrypointValue.slice(0, -3).replace(/[\\/]+/g, ".").replace(/(^|\.)__init__$/, "")
|
|
8433
|
+
);
|
|
8434
|
+
throw subscriberError(
|
|
8435
|
+
`${label} has invalid entrypoint "${entrypointValue}". Use an import path, not a file path${suggestion ? `: "${suggestion.moduleName}"` : ""}`
|
|
8436
|
+
);
|
|
8437
|
+
}
|
|
8438
|
+
const entrypoint = parseModuleEntrypoint(entrypointValue) ?? (requireVariable ? null : parseBareModuleEntrypoint(entrypointValue));
|
|
8330
8439
|
if (!entrypoint) {
|
|
8331
8440
|
throw subscriberError(
|
|
8332
|
-
`${label} has invalid entrypoint "${entrypointValue}". Use "module:object"`
|
|
8441
|
+
`${label} has invalid entrypoint "${entrypointValue}". Use ${requireVariable ? '"module:object"' : '"module:object" or a module path like "pkg.module"'}`
|
|
8333
8442
|
);
|
|
8334
8443
|
}
|
|
8335
8444
|
const name = getModuleEntrypointName(entrypoint);
|
|
@@ -8342,11 +8451,12 @@ async function parseSubscriberEntrypoint(workPath, label, entrypointValue) {
|
|
|
8342
8451
|
`subscriber "${name}" has entrypoint "${entrypointValue}" but file "${entrypoint.filePath}" does not exist`
|
|
8343
8452
|
);
|
|
8344
8453
|
}
|
|
8454
|
+
const { variableName } = entrypoint;
|
|
8345
8455
|
return {
|
|
8346
8456
|
name,
|
|
8347
8457
|
entrypoint: existingEntrypoint,
|
|
8348
8458
|
moduleName: entrypoint.moduleName,
|
|
8349
|
-
variableName:
|
|
8459
|
+
...variableName === void 0 ? {} : { variableName }
|
|
8350
8460
|
};
|
|
8351
8461
|
}
|
|
8352
8462
|
async function parseSubscriber(workPath, index, config) {
|
|
@@ -8362,7 +8472,8 @@ async function parseSubscriber(workPath, index, config) {
|
|
|
8362
8472
|
const base = await parseSubscriberEntrypoint(
|
|
8363
8473
|
workPath,
|
|
8364
8474
|
label,
|
|
8365
|
-
config.entrypoint
|
|
8475
|
+
config.entrypoint,
|
|
8476
|
+
{ requireVariable: false }
|
|
8366
8477
|
);
|
|
8367
8478
|
return {
|
|
8368
8479
|
...base,
|
|
@@ -8382,7 +8493,8 @@ async function parseLegacySubscriber(workPath, index, config) {
|
|
|
8382
8493
|
const base = await parseSubscriberEntrypoint(
|
|
8383
8494
|
workPath,
|
|
8384
8495
|
label,
|
|
8385
|
-
config.entrypoint
|
|
8496
|
+
config.entrypoint,
|
|
8497
|
+
{ requireVariable: true }
|
|
8386
8498
|
);
|
|
8387
8499
|
return {
|
|
8388
8500
|
...base,
|
|
@@ -8446,14 +8558,26 @@ function parseTopicPatterns(name, value) {
|
|
|
8446
8558
|
}
|
|
8447
8559
|
return value;
|
|
8448
8560
|
}
|
|
8449
|
-
function createQueueIntrospectionScript(
|
|
8561
|
+
function createQueueIntrospectionScript(declaration, integrations) {
|
|
8562
|
+
const { variableName } = declaration;
|
|
8563
|
+
const probeLines = variableName === void 0 ? [] : integrations.filter((integration) => integration.subscriberProbe).flatMap(({ module: module2, subscriberProbe }) => [
|
|
8564
|
+
"try:",
|
|
8565
|
+
` from ${module2} import ${subscriberProbe}`,
|
|
8566
|
+
"except ImportError:",
|
|
8567
|
+
" pass",
|
|
8568
|
+
"else:",
|
|
8569
|
+
` if ${subscriberProbe}(${JSON.stringify(declaration.moduleName)}, ${JSON.stringify(variableName)}):`,
|
|
8570
|
+
` owners.append(${JSON.stringify(module2)})`
|
|
8571
|
+
]);
|
|
8450
8572
|
return [
|
|
8451
|
-
"import importlib, json, sys",
|
|
8573
|
+
"import importlib, json, os, sys",
|
|
8574
|
+
`os.environ[${JSON.stringify(SUBSCRIBER_ID_ENV)}] = ${JSON.stringify(declaration.name)}`,
|
|
8575
|
+
'os.environ["VERCEL_APSCHEDULER_DISCOVERY"] = "1"',
|
|
8452
8576
|
...createIntegrationInstallLines(integrations, {
|
|
8453
8577
|
serving: false,
|
|
8454
8578
|
beforeImport: true
|
|
8455
8579
|
}),
|
|
8456
|
-
`importlib.import_module(${JSON.stringify(moduleName)})`,
|
|
8580
|
+
`importlib.import_module(${JSON.stringify(declaration.moduleName)})`,
|
|
8457
8581
|
...createIntegrationInstallLines(integrations, {
|
|
8458
8582
|
serving: false,
|
|
8459
8583
|
beforeImport: false
|
|
@@ -8470,7 +8594,9 @@ function createQueueIntrospectionScript(moduleName, integrations) {
|
|
|
8470
8594
|
" }.items() if v is not None}",
|
|
8471
8595
|
" for s in get_subscriptions()",
|
|
8472
8596
|
"]",
|
|
8473
|
-
"
|
|
8597
|
+
"owners = []",
|
|
8598
|
+
...probeLines,
|
|
8599
|
+
"json.dump({'subscriptions': subs, 'owners': owners}, sys.stdout)"
|
|
8474
8600
|
].join("\n");
|
|
8475
8601
|
}
|
|
8476
8602
|
async function introspectQueueSubscriptions({
|
|
@@ -8481,10 +8607,7 @@ async function introspectQueueSubscriptions({
|
|
|
8481
8607
|
kind,
|
|
8482
8608
|
integrations
|
|
8483
8609
|
}) {
|
|
8484
|
-
const script3 = createQueueIntrospectionScript(
|
|
8485
|
-
declaration.moduleName,
|
|
8486
|
-
integrations
|
|
8487
|
-
);
|
|
8610
|
+
const script3 = createQueueIntrospectionScript(declaration, integrations);
|
|
8488
8611
|
try {
|
|
8489
8612
|
const { stdout } = await uv.run({
|
|
8490
8613
|
venvPath,
|
|
@@ -8516,6 +8639,8 @@ async function introspectQueueSubscriptions({
|
|
|
8516
8639
|
}
|
|
8517
8640
|
async function introspectDevQueueSubscriptions({
|
|
8518
8641
|
moduleName,
|
|
8642
|
+
subscriberName,
|
|
8643
|
+
variableName,
|
|
8519
8644
|
pythonBin,
|
|
8520
8645
|
cwd,
|
|
8521
8646
|
env,
|
|
@@ -8524,7 +8649,13 @@ async function introspectDevQueueSubscriptions({
|
|
|
8524
8649
|
try {
|
|
8525
8650
|
const { stdout } = await (0, import_execa6.default)(
|
|
8526
8651
|
pythonBin,
|
|
8527
|
-
[
|
|
8652
|
+
[
|
|
8653
|
+
"-c",
|
|
8654
|
+
createQueueIntrospectionScript(
|
|
8655
|
+
{ moduleName, name: subscriberName, variableName },
|
|
8656
|
+
integrations
|
|
8657
|
+
)
|
|
8658
|
+
],
|
|
8528
8659
|
// Match the deployed-function environment (see
|
|
8529
8660
|
// introspectQueueSubscriptions): SDKs may need VERCEL=1,
|
|
8530
8661
|
// VERCEL_REGION, and VERCEL_DEPLOYMENT_ID to register subscriptions.
|
|
@@ -8538,12 +8669,12 @@ async function introspectDevQueueSubscriptions({
|
|
|
8538
8669
|
}
|
|
8539
8670
|
}
|
|
8540
8671
|
);
|
|
8541
|
-
const
|
|
8672
|
+
const introspected = parseIntrospectedSubscriptions(
|
|
8542
8673
|
"subscriber",
|
|
8543
8674
|
moduleName,
|
|
8544
8675
|
stdout
|
|
8545
8676
|
);
|
|
8546
|
-
|
|
8677
|
+
const subscriptions = introspected.subscriptions.map((subscription) => {
|
|
8547
8678
|
const { retryAfterSeconds, initialDelaySeconds, maxDeliveries } = subscription.triggerDefaults;
|
|
8548
8679
|
return {
|
|
8549
8680
|
topic: subscription.topic,
|
|
@@ -8553,6 +8684,7 @@ async function introspectDevQueueSubscriptions({
|
|
|
8553
8684
|
...maxDeliveries === void 0 ? {} : { maxDeliveries }
|
|
8554
8685
|
};
|
|
8555
8686
|
});
|
|
8687
|
+
return { subscriptions, owners: introspected.owners };
|
|
8556
8688
|
} catch (err) {
|
|
8557
8689
|
(0, import_build_utils14.debug)(
|
|
8558
8690
|
`Failed to introspect dev queue subscriptions for module "${moduleName}": ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8569,14 +8701,28 @@ function parseIntrospectedSubscriptions(kind, subscriberName, stdout) {
|
|
|
8569
8701
|
`${kind} "${subscriberName}" queue introspection did not return valid JSON`
|
|
8570
8702
|
);
|
|
8571
8703
|
}
|
|
8572
|
-
if (!Array.isArray(parsed)) {
|
|
8704
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8573
8705
|
throw subscriberError(
|
|
8574
|
-
`${kind} "${subscriberName}" queue introspection must return an
|
|
8706
|
+
`${kind} "${subscriberName}" queue introspection must return an object`
|
|
8575
8707
|
);
|
|
8576
8708
|
}
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8709
|
+
const { subscriptions, owners = [] } = parsed;
|
|
8710
|
+
if (!Array.isArray(subscriptions)) {
|
|
8711
|
+
throw subscriberError(
|
|
8712
|
+
`${kind} "${subscriberName}" queue introspection must return a "subscriptions" array`
|
|
8713
|
+
);
|
|
8714
|
+
}
|
|
8715
|
+
if (!Array.isArray(owners) || owners.some((o) => typeof o !== "string")) {
|
|
8716
|
+
throw subscriberError(
|
|
8717
|
+
`${kind} "${subscriberName}" queue introspection "owners" must be an array of strings`
|
|
8718
|
+
);
|
|
8719
|
+
}
|
|
8720
|
+
return {
|
|
8721
|
+
subscriptions: subscriptions.map(
|
|
8722
|
+
(subscription, index) => parseIntrospectedSubscription(kind, subscriberName, index, subscription)
|
|
8723
|
+
),
|
|
8724
|
+
owners
|
|
8725
|
+
};
|
|
8580
8726
|
}
|
|
8581
8727
|
function parseIntrospectedSubscription(kind, subscriberName, index, value) {
|
|
8582
8728
|
const label = `${kind} "${subscriberName}" introspected subscription #${index + 1}`;
|
|
@@ -8620,12 +8766,27 @@ function getQueueWildcardPrefix(pattern) {
|
|
|
8620
8766
|
}
|
|
8621
8767
|
return void 0;
|
|
8622
8768
|
}
|
|
8769
|
+
var APSCHEDULER_INTEGRATION_MODULE = "vercel.integrations.apscheduler";
|
|
8770
|
+
function apschedulerSubscriberIdentities(subscribers) {
|
|
8771
|
+
return subscribers.filter(
|
|
8772
|
+
(subscriber) => subscriber.owners.includes(APSCHEDULER_INTEGRATION_MODULE)
|
|
8773
|
+
).map((subscriber) => ({
|
|
8774
|
+
id: subscriber.name,
|
|
8775
|
+
entrypoint: `${subscriber.moduleName}:${subscriber.variableName}`
|
|
8776
|
+
}));
|
|
8777
|
+
}
|
|
8623
8778
|
function subscriberError(message) {
|
|
8624
8779
|
return new import_build_utils14.NowBuildError({
|
|
8625
8780
|
code: "PYTHON_INVALID_SUBSCRIBER_CONFIG",
|
|
8626
8781
|
message
|
|
8627
8782
|
});
|
|
8628
8783
|
}
|
|
8784
|
+
function apschedulerError(message) {
|
|
8785
|
+
return new import_build_utils14.NowBuildError({
|
|
8786
|
+
code: "PYTHON_INVALID_APSCHEDULER_CONFIG",
|
|
8787
|
+
message
|
|
8788
|
+
});
|
|
8789
|
+
}
|
|
8629
8790
|
|
|
8630
8791
|
// src/start-dev-server.ts
|
|
8631
8792
|
var DEV_SERVER_STARTUP_TIMEOUT = 5 * 6e4;
|
|
@@ -9087,6 +9248,7 @@ var startDevServer = async (opts) => {
|
|
|
9087
9248
|
const isPyprojectEntrypoint = entrypoint === "pyproject.toml";
|
|
9088
9249
|
let resolved;
|
|
9089
9250
|
const handlerFunction = typeof config?.handlerFunction === "string" ? config.handlerFunction : void 0;
|
|
9251
|
+
const isPyQueueSidecar = config?.pythonQueueSidecar === "subscriber" || config?.pythonQueueSidecar === "workflow";
|
|
9090
9252
|
let detected;
|
|
9091
9253
|
if (isPyprojectEntrypoint) {
|
|
9092
9254
|
const declaredEntrypoint = await getVercelToolsEntrypoint(
|
|
@@ -9102,7 +9264,7 @@ var startDevServer = async (opts) => {
|
|
|
9102
9264
|
filePath: entrypoint,
|
|
9103
9265
|
// Schedule-triggered services create their own "app" wrapper dynamically.
|
|
9104
9266
|
// Other services use handlerFunction as the entrypoint variable name.
|
|
9105
|
-
varName: service && (0, import_build_utils15.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
|
|
9267
|
+
varName: service && (0, import_build_utils15.isScheduleTriggeredService)(service) ? void 0 : handlerFunction ?? (isPyQueueSidecar ? "app" : void 0)
|
|
9106
9268
|
} : void 0,
|
|
9107
9269
|
service,
|
|
9108
9270
|
opts.repoRootPath
|
|
@@ -9241,7 +9403,13 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
9241
9403
|
entrypointDir: workPath,
|
|
9242
9404
|
rootDir: workPath
|
|
9243
9405
|
});
|
|
9244
|
-
|
|
9406
|
+
const queueAdapterBootstrap = await getQueueAdapterBootstrap({
|
|
9407
|
+
pythonPackage,
|
|
9408
|
+
env,
|
|
9409
|
+
legacyWorkersProject: legacyProject,
|
|
9410
|
+
hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
|
|
9411
|
+
});
|
|
9412
|
+
queueIntegrations = queueAdapterBootstrap.integrations;
|
|
9245
9413
|
if (queueIntegrations.length > 0) {
|
|
9246
9414
|
env.VERCEL_QUEUE_INTEGRATIONS = queueIntegrations.map(
|
|
9247
9415
|
({ module: module2, installer, servingActivator }) => `${module2}:${installer}` + (servingActivator ? `:${servingActivator}` : "")
|
|
@@ -9249,11 +9417,7 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
9249
9417
|
} else {
|
|
9250
9418
|
delete env.VERCEL_QUEUE_INTEGRATIONS;
|
|
9251
9419
|
}
|
|
9252
|
-
const
|
|
9253
|
-
pythonPackage,
|
|
9254
|
-
env
|
|
9255
|
-
});
|
|
9256
|
-
for (const injectedPackage of conditionalInjectedPackages) {
|
|
9420
|
+
for (const injectedPackage of queueAdapterBootstrap.injectedPackages) {
|
|
9257
9421
|
await installInjectedDevPackage(
|
|
9258
9422
|
{
|
|
9259
9423
|
name: injectedPackage.name,
|
|
@@ -9268,6 +9432,48 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
9268
9432
|
`Skipping conditional dev package injection: ${err instanceof Error ? err.message : String(err)}`
|
|
9269
9433
|
);
|
|
9270
9434
|
}
|
|
9435
|
+
const apschedulerSubscribers = [];
|
|
9436
|
+
if (!legacyProject) {
|
|
9437
|
+
const runtimeDir = (0, import_path13.join)(workPath, ".vercel", "python");
|
|
9438
|
+
const declaredSubscribers = await getPyprojectSubscribers(workPath);
|
|
9439
|
+
const introspectionEnv = {
|
|
9440
|
+
...env,
|
|
9441
|
+
PYTHONPATH: [runtimeDir, env.PYTHONPATH].filter(Boolean).join(import_path13.delimiter)
|
|
9442
|
+
};
|
|
9443
|
+
delete introspectionEnv.VERCEL_APSCHEDULER_SUBSCRIBERS;
|
|
9444
|
+
for (const declaration of declaredSubscribers) {
|
|
9445
|
+
const introspected = await introspectDevQueueSubscriptions({
|
|
9446
|
+
moduleName: declaration.moduleName,
|
|
9447
|
+
subscriberName: declaration.name,
|
|
9448
|
+
variableName: declaration.variableName || "app",
|
|
9449
|
+
pythonBin: spawnCommand,
|
|
9450
|
+
cwd: workPath,
|
|
9451
|
+
env: introspectionEnv,
|
|
9452
|
+
integrations: queueIntegrations
|
|
9453
|
+
});
|
|
9454
|
+
apschedulerSubscribers.push(
|
|
9455
|
+
...apschedulerSubscriberIdentities([
|
|
9456
|
+
{ ...declaration, owners: introspected?.owners ?? [] }
|
|
9457
|
+
])
|
|
9458
|
+
);
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
if (apschedulerSubscribers.length > 0) {
|
|
9462
|
+
env.VERCEL_APSCHEDULER_SUBSCRIBERS = JSON.stringify(
|
|
9463
|
+
apschedulerSubscribers
|
|
9464
|
+
);
|
|
9465
|
+
const previewConfig = await getApschedulerPreviewConfig(workPath);
|
|
9466
|
+
if (previewConfig) {
|
|
9467
|
+
env.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS = String(
|
|
9468
|
+
previewConfig.idleTimeoutSeconds
|
|
9469
|
+
);
|
|
9470
|
+
} else {
|
|
9471
|
+
delete env.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS;
|
|
9472
|
+
}
|
|
9473
|
+
} else {
|
|
9474
|
+
delete env.VERCEL_APSCHEDULER_SUBSCRIBERS;
|
|
9475
|
+
delete env.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS;
|
|
9476
|
+
}
|
|
9271
9477
|
if (legacyProject) {
|
|
9272
9478
|
await installInjectedDevPackage(
|
|
9273
9479
|
{
|
|
@@ -9292,19 +9498,29 @@ If you are using a virtual environment, activate it before running "vercel dev",
|
|
|
9292
9498
|
}
|
|
9293
9499
|
if (useQueueServing) {
|
|
9294
9500
|
env.VERCEL_DEV_QUEUE_SERVING = "1";
|
|
9501
|
+
const subscriberName = getModuleEntrypointName({
|
|
9502
|
+
moduleName: modulePath,
|
|
9503
|
+
variableName: variableName || "app"
|
|
9504
|
+
});
|
|
9505
|
+
env.VERCEL_PYTHON_SUBSCRIBER_ID = subscriberName;
|
|
9295
9506
|
const runtimeDir = (0, import_path13.join)(workPath, ".vercel", "python");
|
|
9296
|
-
|
|
9507
|
+
const introspectionEnv = {
|
|
9508
|
+
...env,
|
|
9509
|
+
PYTHONPATH: [runtimeDir, env.PYTHONPATH].filter(Boolean).join(import_path13.delimiter)
|
|
9510
|
+
};
|
|
9511
|
+
delete introspectionEnv.VERCEL_APSCHEDULER_SUBSCRIBERS;
|
|
9512
|
+
queueSubscriptions = (await introspectDevQueueSubscriptions({
|
|
9297
9513
|
moduleName: modulePath,
|
|
9514
|
+
subscriberName,
|
|
9515
|
+
variableName: variableName || "app",
|
|
9298
9516
|
pythonBin: spawnCommand,
|
|
9299
9517
|
cwd: workPath,
|
|
9300
|
-
env:
|
|
9301
|
-
...env,
|
|
9302
|
-
PYTHONPATH: [runtimeDir, env.PYTHONPATH].filter(Boolean).join(import_path13.delimiter)
|
|
9303
|
-
},
|
|
9518
|
+
env: introspectionEnv,
|
|
9304
9519
|
integrations: queueIntegrations
|
|
9305
|
-
});
|
|
9520
|
+
}))?.subscriptions;
|
|
9306
9521
|
} else {
|
|
9307
9522
|
delete env.VERCEL_DEV_QUEUE_SERVING;
|
|
9523
|
+
delete env.VERCEL_PYTHON_SUBSCRIBER_ID;
|
|
9308
9524
|
await installInjectedDevPackage(
|
|
9309
9525
|
{
|
|
9310
9526
|
name: "vercel-workers",
|
|
@@ -10793,7 +11009,7 @@ async function getDevSidecars({
|
|
|
10793
11009
|
use: build2.use,
|
|
10794
11010
|
src: subscriber.entrypoint,
|
|
10795
11011
|
config: {
|
|
10796
|
-
handlerFunction: subscriber.variableName,
|
|
11012
|
+
...subscriber.variableName === void 0 ? {} : { handlerFunction: subscriber.variableName },
|
|
10797
11013
|
pythonQueueSidecar: "subscriber"
|
|
10798
11014
|
}
|
|
10799
11015
|
},
|
|
@@ -11420,11 +11636,14 @@ var build = async ({
|
|
|
11420
11636
|
projectDir: (0, import_path21.join)(workPath, entryDirectory),
|
|
11421
11637
|
pipPlatformArgs
|
|
11422
11638
|
});
|
|
11423
|
-
const
|
|
11639
|
+
const queueAdapterBootstrap = await getQueueAdapterBootstrap({
|
|
11424
11640
|
pythonPackage,
|
|
11425
|
-
env: baseEnv
|
|
11426
|
-
|
|
11427
|
-
|
|
11641
|
+
env: baseEnv,
|
|
11642
|
+
legacyWorkersProject,
|
|
11643
|
+
hasDeclaredSubscribers: await hasPyprojectSubscribers(workPath)
|
|
11644
|
+
});
|
|
11645
|
+
const queueAdapterInjectedPackages = usedUvManagedInstall ? queueAdapterBootstrap.injectedPackages : [];
|
|
11646
|
+
for (const injectedPackage of queueAdapterInjectedPackages) {
|
|
11428
11647
|
await installInjectedPackage({
|
|
11429
11648
|
...injectedPackage,
|
|
11430
11649
|
uv,
|
|
@@ -11465,7 +11684,7 @@ var build = async ({
|
|
|
11465
11684
|
if (quirksResult.buildEnv) {
|
|
11466
11685
|
Object.assign(pythonEnv, quirksResult.buildEnv);
|
|
11467
11686
|
}
|
|
11468
|
-
const queueIntegrations =
|
|
11687
|
+
const queueIntegrations = queueAdapterBootstrap.integrations;
|
|
11469
11688
|
const writeGeneratedQueueHandler = async (outputPath, declaration) => {
|
|
11470
11689
|
const generatedPath = getGeneratedQueueHandlerPath(outputPath);
|
|
11471
11690
|
await import_fs20.default.promises.mkdir((0, import_path21.dirname)((0, import_path21.join)(workPath, generatedPath)), {
|
|
@@ -11635,6 +11854,18 @@ var build = async ({
|
|
|
11635
11854
|
({ module: module2, installer, servingActivator }) => `${module2}:${installer}` + (servingActivator ? `:${servingActivator}` : "")
|
|
11636
11855
|
).join(",");
|
|
11637
11856
|
}
|
|
11857
|
+
const apschedulerSubscribers = apschedulerSubscriberIdentities(subscribers);
|
|
11858
|
+
if (apschedulerSubscribers.length > 0) {
|
|
11859
|
+
lambdaEnv.VERCEL_APSCHEDULER_SUBSCRIBERS = JSON.stringify(
|
|
11860
|
+
apschedulerSubscribers
|
|
11861
|
+
);
|
|
11862
|
+
const previewConfig = await getApschedulerPreviewConfig(workPath);
|
|
11863
|
+
if (previewConfig) {
|
|
11864
|
+
lambdaEnv.VERCEL_APSCHEDULER_PREVIEW_IDLE_TIMEOUT_SECONDS = String(
|
|
11865
|
+
previewConfig.idleTimeoutSeconds
|
|
11866
|
+
);
|
|
11867
|
+
}
|
|
11868
|
+
}
|
|
11638
11869
|
const globOptions = {
|
|
11639
11870
|
cwd: workPath,
|
|
11640
11871
|
ignore: config && typeof config.excludeFiles === "string" ? [...predefinedExcludes, config.excludeFiles] : predefinedExcludes
|
|
@@ -11971,7 +12202,9 @@ var build = async ({
|
|
|
11971
12202
|
runtime: pythonVersion.runtime,
|
|
11972
12203
|
...lambdaOptions,
|
|
11973
12204
|
architecture: target.architecture,
|
|
11974
|
-
environment:
|
|
12205
|
+
environment: {
|
|
12206
|
+
...lambdaEnv
|
|
12207
|
+
},
|
|
11975
12208
|
supportsResponseStreaming: true
|
|
11976
12209
|
});
|
|
11977
12210
|
}
|
|
@@ -12009,7 +12242,12 @@ var build = async ({
|
|
|
12009
12242
|
handler: `${handlerPyFilename}.vc_handler`,
|
|
12010
12243
|
runtime: pythonVersion.runtime,
|
|
12011
12244
|
architecture: target.architecture,
|
|
12012
|
-
environment:
|
|
12245
|
+
environment: {
|
|
12246
|
+
...lambdaEnv,
|
|
12247
|
+
// The runtime activates publish-side integrations before importing the
|
|
12248
|
+
// generated subscriber module, so the identity must already be present.
|
|
12249
|
+
VERCEL_PYTHON_SUBSCRIBER_ID: subscriber.name
|
|
12250
|
+
},
|
|
12013
12251
|
experimentalTriggers,
|
|
12014
12252
|
supportsResponseStreaming: true
|
|
12015
12253
|
});
|
|
@@ -12021,6 +12259,11 @@ var build = async ({
|
|
|
12021
12259
|
const consumer = getSubscriberConsumerName(declaration.name);
|
|
12022
12260
|
const legacy = declaration.legacy;
|
|
12023
12261
|
(0, import_assert.default)(legacy, "legacy subscriber declarations must carry legacy config");
|
|
12262
|
+
const { variableName } = declaration;
|
|
12263
|
+
(0, import_assert.default)(
|
|
12264
|
+
variableName,
|
|
12265
|
+
"legacy subscriber declarations must name an entrypoint object"
|
|
12266
|
+
);
|
|
12024
12267
|
const experimentalTriggers = legacy.topics.map((topic) => ({
|
|
12025
12268
|
type: "queue/v2beta",
|
|
12026
12269
|
topic,
|
|
@@ -12035,7 +12278,7 @@ var build = async ({
|
|
|
12035
12278
|
moduleName: declaration.moduleName,
|
|
12036
12279
|
entrypoint: declaration.entrypoint,
|
|
12037
12280
|
vendorDir,
|
|
12038
|
-
variableName
|
|
12281
|
+
variableName
|
|
12039
12282
|
})
|
|
12040
12283
|
})
|
|
12041
12284
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/python",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.56.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
|
@@ -35,18 +35,15 @@
|
|
|
35
35
|
"smol-toml": "1.5.2",
|
|
36
36
|
"vitest": "2.1.4",
|
|
37
37
|
"which": "3.0.0",
|
|
38
|
+
"@vercel/build-utils": "14.0.3",
|
|
38
39
|
"@vercel/error-utils": "2.2.1",
|
|
39
|
-
"@vercel/
|
|
40
|
-
"@vercel/python-runtime": "0.18.0"
|
|
40
|
+
"@vercel/python-runtime": "0.19.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "node ../../utils/build-builder.mjs",
|
|
44
44
|
"type-check": "tsc --noEmit",
|
|
45
45
|
"test": "cross-env VERCEL_FORCE_PYTHON_STREAMING=1 NODE_OPTIONS=--experimental-vm-modules vitest run --config ../../vitest.config.mts",
|
|
46
|
-
"test-unit": "vitest run --config ../../vitest.config.mts test/unit",
|
|
47
|
-
"test-e2e": "
|
|
48
|
-
"vitest-run": "vitest -c ../../vitest.config.mts",
|
|
49
|
-
"vitest-unit": "pnpm test test/unit",
|
|
50
|
-
"vitest-e2e": "glob --absolute 'test/integration-*'"
|
|
46
|
+
"test-unit": "cross-env VERCEL_FORCE_PYTHON_STREAMING=1 NODE_OPTIONS=--experimental-vm-modules vitest run --config ../../vitest.config.mts test/unit",
|
|
47
|
+
"test-e2e": "cross-env VERCEL_FORCE_PYTHON_STREAMING=1 NODE_OPTIONS=--experimental-vm-modules vitest run --config ../../vitest.config.mts test/integration-"
|
|
51
48
|
}
|
|
52
49
|
}
|