@vercel/python 6.48.0 → 6.49.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.
Files changed (2) hide show
  1. package/dist/index.js +284 -128
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -4366,6 +4366,7 @@ __export(src_exports, {
4366
4366
  detectEntrypoint: () => detectEntrypoint,
4367
4367
  diagnostics: () => diagnostics,
4368
4368
  downloadFilesInWorkPath: () => downloadFilesInWorkPath,
4369
+ getDevSidecars: () => getDevSidecars,
4369
4370
  installRequirement: () => installRequirement,
4370
4371
  installRequirementsFile: () => installRequirementsFile,
4371
4372
  prepareCache: () => prepareCache,
@@ -5649,33 +5650,26 @@ var import_execa4 = __toESM(require_execa());
5649
5650
  var import_build_utils6 = require("@vercel/build-utils");
5650
5651
  var import_fs5 = __toESM(require("fs"));
5651
5652
  var import_path6 = require("path");
5652
-
5653
- // src/large-functions.ts
5654
- function isLargeFunctionsEnabled() {
5655
- const value = process.env.VERCEL_SUPPORT_LARGE_FUNCTIONS;
5656
- return value === "1" || value === "true";
5657
- }
5658
-
5659
- // src/compileall.ts
5660
- function isCompileAllEnabled() {
5661
- if (!isLargeFunctionsEnabled())
5662
- return false;
5653
+ var COMPILEALL_TIMEOUT_MS = 5 * 60 * 1e3;
5654
+ function isCompileAllFlagEnabled() {
5663
5655
  const val = process.env.VERCEL_PYTHON_COMPILEALL;
5664
5656
  if (val === void 0 || val === "")
5665
5657
  return false;
5666
5658
  const lower = val.toLowerCase();
5667
5659
  return lower === "1" || lower === "true";
5668
5660
  }
5669
- function shouldUseCompileAll({
5661
+ function shouldCompileAll({
5670
5662
  isDev,
5671
5663
  hasCustomCommand,
5672
- hasCustomBuildCommand
5664
+ hasPreDeployCommand
5673
5665
  }) {
5674
5666
  if (isDev)
5675
5667
  return false;
5676
- if (hasCustomCommand || hasCustomBuildCommand)
5668
+ if (hasCustomCommand)
5669
+ return false;
5670
+ if (hasPreDeployCommand)
5677
5671
  return false;
5678
- return isCompileAllEnabled();
5672
+ return isCompileAllFlagEnabled();
5679
5673
  }
5680
5674
  async function runCompileAll({
5681
5675
  pythonBin,
@@ -5698,7 +5692,10 @@ async function runCompileAll({
5698
5692
  ...filesOrDirectories
5699
5693
  ];
5700
5694
  try {
5701
- await (0, import_execa4.default)(pythonBin, args, { env: env || process.env });
5695
+ await (0, import_execa4.default)(pythonBin, args, {
5696
+ env: env || process.env,
5697
+ timeout: COMPILEALL_TIMEOUT_MS
5698
+ });
5702
5699
  } catch (err) {
5703
5700
  (0, import_build_utils6.debug)(`compileall error details: ${JSON.stringify(err)}`);
5704
5701
  }
@@ -5774,6 +5771,12 @@ async function collectAppBytecodeFiles({
5774
5771
  return { files, totalSize, perItemSizes };
5775
5772
  }
5776
5773
 
5774
+ // src/large-functions.ts
5775
+ function isLargeFunctionsEnabled() {
5776
+ const value = process.env.VERCEL_SUPPORT_LARGE_FUNCTIONS;
5777
+ return value === "1" || value === "true";
5778
+ }
5779
+
5777
5780
  // src/dependency-externalizer.ts
5778
5781
  var readFile = (0, import_util.promisify)(import_fs6.default.readFile);
5779
5782
  var STRIP_BASENAMES = /* @__PURE__ */ new Set([
@@ -5794,6 +5797,8 @@ function shouldStripVendorFile(filePath) {
5794
5797
  return false;
5795
5798
  }
5796
5799
  var LAMBDA_SIZE_THRESHOLD_BYTES = 225 * 1024 * 1024;
5800
+ var BYTECODE_FILL_MARGIN_BYTES = 5 * 1024 * 1024;
5801
+ var BYTECODE_FILL_CEILING_BYTES = LAMBDA_SIZE_THRESHOLD_BYTES - BYTECODE_FILL_MARGIN_BYTES;
5797
5802
  var LAMBDA_EPHEMERAL_STORAGE_BYTES = 500 * 1024 * 1024;
5798
5803
  var MAX_LARGE_FUNCTION_UNCOMPRESSED_SIZE = 5 * 1024 * 1024 * 1024;
5799
5804
  var BUNDLING_DOCS_LINK = "https://vercel.com/docs/functions/runtimes/python#controlling-what-gets-bundled";
@@ -6510,10 +6515,12 @@ async function getPackagesReachableOnPlatform(lockFile, projectName, pythonMajor
6510
6515
  }
6511
6516
  return visited;
6512
6517
  }
6513
- async function calculateBundleSize(files) {
6518
+ async function calculateBundleSize(files, filter) {
6514
6519
  let knownSize = 0;
6515
6520
  const statPromises = [];
6516
6521
  for (const filePath of Object.keys(files)) {
6522
+ if (filter && !filter(filePath))
6523
+ continue;
6517
6524
  const file = files[filePath];
6518
6525
  if ("fsPath" in file && file.fsPath) {
6519
6526
  const fsRef = file;
@@ -6521,7 +6528,10 @@ async function calculateBundleSize(files) {
6521
6528
  knownSize += fsRef.size;
6522
6529
  } else {
6523
6530
  statPromises.push(
6524
- import_fs6.default.promises.stat(fsRef.fsPath).then((stats) => stats.size).catch((err) => {
6531
+ import_fs6.default.promises.stat(fsRef.fsPath).then((stats) => {
6532
+ fsRef.size = stats.size;
6533
+ return stats.size;
6534
+ }).catch((err) => {
6525
6535
  console.warn(
6526
6536
  `Warning: Failed to stat file ${fsRef.fsPath}, size will not be included in bundle calculation: ${err}`
6527
6537
  );
@@ -6541,6 +6551,12 @@ async function calculateBundleSize(files) {
6541
6551
  }
6542
6552
  return totalSize;
6543
6553
  }
6554
+ var PYC_TO_PY_RATIO = 1.2;
6555
+ var BYTECODE_COVERAGE_FLOOR = 0.5;
6556
+ async function estimateBytecodeSize(files) {
6557
+ const pyBytes = await calculateBundleSize(files, (p) => p.endsWith(".py"));
6558
+ return PYC_TO_PY_RATIO * pyBytes;
6559
+ }
6544
6560
  function lambdaKnapsack(packages, capacity) {
6545
6561
  if (capacity <= 0) {
6546
6562
  return [];
@@ -6870,7 +6886,7 @@ function getMissingExportMessage(framework, entrypoints) {
6870
6886
  }
6871
6887
  }
6872
6888
  function entrypointToModule(entrypoint) {
6873
- return entrypoint.replace(/\\/g, "/").replace(/\.py$/i, "").replace(/\//g, ".");
6889
+ return entrypoint.replace(/\\/g, "/").replace(/\.py$/i, "").replace(/\/__init__$/i, "").replace(/\//g, ".");
6874
6890
  }
6875
6891
  async function checkEntrypoint(workPath, relPath) {
6876
6892
  const absPath = (0, import_path8.join)(workPath, relPath);
@@ -6946,14 +6962,37 @@ async function resolveModuleAttrEntrypoint(workPath, value) {
6946
6962
  }
6947
6963
  return null;
6948
6964
  }
6949
- async function getVercelToolsEntrypoint(workPath) {
6950
- const pyprojectData = await (0, import_build_utils11.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
6965
+ async function getVercelToolsEntrypoint(workPath, repoRootPath) {
6966
+ const pyprojectPath = (0, import_path8.join)(workPath, "pyproject.toml");
6967
+ const pyprojectData = await (0, import_build_utils11.readConfigFile)(pyprojectPath);
6951
6968
  if (!pyprojectData)
6952
6969
  return null;
6953
6970
  const vercelEntrypoint = pyprojectData.tool?.vercel?.entrypoint;
6954
- if (typeof vercelEntrypoint !== "string")
6971
+ if (vercelEntrypoint === void 0)
6955
6972
  return null;
6956
- return resolveModuleAttrEntrypoint(workPath, vercelEntrypoint);
6973
+ const relPyprojectPath = (0, import_path8.relative)(repoRootPath ?? workPath, pyprojectPath);
6974
+ const displayPath = !relPyprojectPath || relPyprojectPath.startsWith("..") ? pyprojectPath : relPyprojectPath;
6975
+ if (typeof vercelEntrypoint !== "string") {
6976
+ throw new import_build_utils9.NowBuildError({
6977
+ code: "PYTHON_INVALID_ENTRYPOINT",
6978
+ message: `"tool.vercel.entrypoint" in "${displayPath}" must be a string in "module:object" format (e.g. "main:app").`,
6979
+ link: PYTHON_ENTRYPOINT_DOCS_URL,
6980
+ action: "Learn More"
6981
+ });
6982
+ }
6983
+ const resolved = await resolveModuleAttrEntrypoint(
6984
+ workPath,
6985
+ vercelEntrypoint
6986
+ );
6987
+ if (!resolved) {
6988
+ throw new import_build_utils9.NowBuildError({
6989
+ code: "PYTHON_ENTRYPOINT_NOT_FOUND",
6990
+ message: `"tool.vercel.entrypoint" in "${displayPath}" is "${vercelEntrypoint}" but no matching module file was found. Use "module:object" format (e.g. "main:app") and ensure the module file exists, or remove the setting to use automatic entrypoint detection.`,
6991
+ link: PYTHON_ENTRYPOINT_DOCS_URL,
6992
+ action: "Learn More"
6993
+ });
6994
+ }
6995
+ return resolved;
6957
6996
  }
6958
6997
  async function getPyprojectEntrypointWithDiagnostics(workPath) {
6959
6998
  const pyprojectData = await (0, import_build_utils11.readConfigFile)((0, import_path8.join)(workPath, "pyproject.toml"));
@@ -7107,7 +7146,7 @@ async function detectDjangoPythonEntrypoint(workPath) {
7107
7146
  return emptyResult;
7108
7147
  }
7109
7148
  }
7110
- async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint, service) {
7149
+ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint, service, repoRootPath) {
7111
7150
  if (configuredEntrypoint) {
7112
7151
  const { filePath: configEntryFile, varName: configEntryVar } = configuredEntrypoint;
7113
7152
  const entrypoint = configEntryFile.endsWith(".py") ? configEntryFile : `${configEntryFile}.py`;
@@ -7146,7 +7185,7 @@ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint,
7146
7185
  if (!framework) {
7147
7186
  return null;
7148
7187
  }
7149
- const vercelEntry = await getVercelToolsEntrypoint(workPath);
7188
+ const vercelEntry = await getVercelToolsEntrypoint(workPath, repoRootPath);
7150
7189
  if (vercelEntry)
7151
7190
  return { entrypoint: vercelEntry };
7152
7191
  let findDiagnostics;
@@ -7186,10 +7225,18 @@ var detectEntrypoint = async ({
7186
7225
  }) => {
7187
7226
  if (!(0, import_build_utils9.isPythonFramework)(framework))
7188
7227
  return null;
7189
- const detected = await detectPythonEntrypoint(
7190
- framework,
7191
- workPath
7192
- );
7228
+ let detected;
7229
+ try {
7230
+ detected = await detectPythonEntrypoint(
7231
+ framework,
7232
+ workPath
7233
+ );
7234
+ } catch (err) {
7235
+ (0, import_build_utils10.debug)(
7236
+ `Python entrypoint detection failed for ${workPath}: ${err instanceof Error ? err.message : String(err)}`
7237
+ );
7238
+ return null;
7239
+ }
7193
7240
  if (!detected?.entrypoint)
7194
7241
  return null;
7195
7242
  const { entrypoint, variableName } = detected.entrypoint;
@@ -7415,6 +7462,23 @@ async function getReachableHost(port) {
7415
7462
  ]);
7416
7463
  return results.find(Boolean) || false;
7417
7464
  }
7465
+ async function dedupePendingOperation(operations, key, operation) {
7466
+ const existing = operations.get(key);
7467
+ if (existing) {
7468
+ return existing;
7469
+ }
7470
+ const pending = operation();
7471
+ operations.set(key, pending);
7472
+ try {
7473
+ return await pending;
7474
+ } finally {
7475
+ if (operations.get(key) === pending) {
7476
+ operations.delete(key);
7477
+ }
7478
+ }
7479
+ }
7480
+ var PENDING_MANAGED_VENV_CREATIONS = /* @__PURE__ */ new Map();
7481
+ var PENDING_DEPENDENCY_SYNCS = /* @__PURE__ */ new Map();
7418
7482
  async function syncDependencies({
7419
7483
  workPath,
7420
7484
  uvPath,
@@ -7558,16 +7622,29 @@ async function runSync({
7558
7622
  });
7559
7623
  }
7560
7624
  var PENDING_INSTALLS = /* @__PURE__ */ new Map();
7625
+ var COMPLETED_INSTALLS = /* @__PURE__ */ new Set();
7626
+ function hasInstalledDistribution(targetDir, packageName) {
7627
+ const prefix = `${packageName.replace("-", "_")}-`;
7628
+ try {
7629
+ return (0, import_fs10.readdirSync)(targetDir).some(
7630
+ (entry) => entry.startsWith(prefix) && entry.endsWith(".dist-info")
7631
+ );
7632
+ } catch {
7633
+ return false;
7634
+ }
7635
+ }
7561
7636
  async function installInjectedDevPackage(pkg, opts) {
7562
7637
  const targetDir = (0, import_path10.join)(opts.workPath, ".vercel", "python");
7563
- const key = `${targetDir}:${pkg.name}`;
7564
- let pending = PENDING_INSTALLS.get(key);
7565
- if (!pending) {
7566
- pending = doInstallInjectedDevPackage(pkg, { ...opts, targetDir });
7567
- PENDING_INSTALLS.set(key, pending);
7568
- pending.finally(() => PENDING_INSTALLS.delete(key));
7638
+ const source = pkg.envOverride || pkg.pinnedVersion;
7639
+ const key = `${targetDir}:${pkg.name}:${source}`;
7640
+ if (COMPLETED_INSTALLS.has(key) && hasInstalledDistribution(targetDir, pkg.name)) {
7641
+ return;
7569
7642
  }
7570
- await pending;
7643
+ COMPLETED_INSTALLS.delete(key);
7644
+ await dedupePendingOperation(PENDING_INSTALLS, key, async () => {
7645
+ await doInstallInjectedDevPackage(pkg, { ...opts, targetDir });
7646
+ COMPLETED_INSTALLS.add(key);
7647
+ });
7571
7648
  }
7572
7649
  async function doInstallInjectedDevPackage(pkg, opts) {
7573
7650
  const { targetDir, workPath, uvPath, pythonBin, env, onStdout, onStderr } = opts;
@@ -7707,18 +7784,26 @@ function createDevShim(workPath, entry, modulePath, serviceName, framework, vari
7707
7784
  }
7708
7785
  }
7709
7786
  async function getMultiServicePythonRunner(workPath, env, systemPython, uvPath) {
7787
+ const venvPath = (0, import_path10.join)(workPath, ".venv");
7788
+ const pendingCreation = PENDING_MANAGED_VENV_CREATIONS.get(venvPath);
7789
+ if (pendingCreation) {
7790
+ await pendingCreation;
7791
+ }
7710
7792
  const { pythonCmd, venvRoot } = useVirtualEnv(workPath, env, systemPython);
7711
7793
  if (venvRoot) {
7712
7794
  (0, import_build_utils13.debug)(`Using existing virtualenv at ${venvRoot} for multi-service dev`);
7713
7795
  return { command: pythonCmd, args: [] };
7714
7796
  }
7715
- const venvPath = (0, import_path10.join)(workPath, ".venv");
7716
- await ensureVenv({
7717
- pythonVersion: { pythonPath: systemPython },
7797
+ await dedupePendingOperation(
7798
+ PENDING_MANAGED_VENV_CREATIONS,
7718
7799
  venvPath,
7719
- uvPath,
7720
- quiet: true
7721
- });
7800
+ () => ensureVenv({
7801
+ pythonVersion: { pythonPath: systemPython },
7802
+ venvPath,
7803
+ uvPath,
7804
+ quiet: true
7805
+ })
7806
+ );
7722
7807
  (0, import_build_utils13.debug)(`Created virtualenv at ${venvPath} for multi-service dev`);
7723
7808
  const pythonBin = getVenvPythonBin(venvPath);
7724
7809
  const binDir = getVenvBinDir(venvPath);
@@ -7779,7 +7864,8 @@ var startDevServer = async (opts) => {
7779
7864
  // Other services use handlerFunction as the entrypoint variable name.
7780
7865
  varName: service && (0, import_build_utils13.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
7781
7866
  } : void 0,
7782
- service
7867
+ service,
7868
+ opts.repoRootPath
7783
7869
  );
7784
7870
  let hookResult;
7785
7871
  if (detected?.entrypoint) {
@@ -7893,7 +7979,11 @@ If you are using a virtual environment, activate it before running "vercel dev",
7893
7979
  } else {
7894
7980
  console.log(syncMessage);
7895
7981
  }
7896
- await syncDependencies(devOpts);
7982
+ await dedupePendingOperation(
7983
+ PENDING_DEPENDENCY_SYNCS,
7984
+ workPath,
7985
+ () => syncDependencies(devOpts)
7986
+ );
7897
7987
  }
7898
7988
  await installInjectedDevPackage(
7899
7989
  {
@@ -8587,8 +8677,8 @@ var import_python_analysis9 = require("@vercel/python-analysis");
8587
8677
  var import_path14 = require("path");
8588
8678
  var import_fs14 = __toESM(require("fs"));
8589
8679
  var import_build_utils18 = require("@vercel/build-utils");
8590
- var SUBSCRIBER_NAME_RE = /^[A-Za-z]([A-Za-z0-9_-]*[A-Za-z0-9])?$/;
8591
8680
  var MODULE_ATTR_RE = /^([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*):([A-Za-z_][\w]*)$/;
8681
+ var SUBSCRIBER_OUTPUT_DIR = "_py_subscribers";
8592
8682
  var TRIGGER_NUMBER_FIELDS = [
8593
8683
  {
8594
8684
  field: "max_deliveries",
@@ -8628,6 +8718,12 @@ function safePathSegment(value) {
8628
8718
  return /[A-Za-z0-9-]/.test(char) ? char : `_${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`;
8629
8719
  }).join("");
8630
8720
  }
8721
+ function getSubscriberOutputPath(subscriberName) {
8722
+ return `${SUBSCRIBER_OUTPUT_DIR}/${safePathSegment(subscriberName)}`;
8723
+ }
8724
+ function getSubscriberConsumerName(subscriberName) {
8725
+ return (0, import_build_utils18.sanitizeConsumerName)(getSubscriberOutputPath(subscriberName));
8726
+ }
8631
8727
  async function getPyprojectSubscribers(workPath) {
8632
8728
  const pyprojectPath = (0, import_path14.join)(workPath, "pyproject.toml");
8633
8729
  if (!import_fs14.default.existsSync(pyprojectPath)) {
@@ -8638,37 +8734,38 @@ async function getPyprojectSubscribers(workPath) {
8638
8734
  if (!subscribers) {
8639
8735
  return [];
8640
8736
  }
8641
- if (typeof subscribers !== "object" || Array.isArray(subscribers)) {
8642
- throw subscriberError('"tool.vercel.subscribers" must be an object');
8737
+ if (!Array.isArray(subscribers)) {
8738
+ throw subscriberError('"tool.vercel.subscribers" must be an array');
8643
8739
  }
8644
- return Promise.all(
8645
- Object.entries(subscribers).map(
8646
- ([name, config]) => parseSubscriber(workPath, name, config)
8647
- )
8740
+ const parsedSubscribers = await Promise.all(
8741
+ subscribers.map((config, index) => parseSubscriber(workPath, index, config))
8648
8742
  );
8649
- }
8650
- async function parseSubscriber(workPath, name, config) {
8651
- if (!SUBSCRIBER_NAME_RE.test(name)) {
8652
- throw subscriberError(
8653
- `subscriber name "${name}" is invalid. Names must start with a letter, end with an alphanumeric character, and contain only alphanumeric characters, hyphens, and underscores`
8654
- );
8743
+ const seenNames = /* @__PURE__ */ new Set();
8744
+ for (const subscriber of parsedSubscribers) {
8745
+ if (seenNames.has(subscriber.name)) {
8746
+ throw subscriberError(
8747
+ `subscriber "${subscriber.name}" is declared more than once`
8748
+ );
8749
+ }
8750
+ seenNames.add(subscriber.name);
8655
8751
  }
8752
+ return parsedSubscribers;
8753
+ }
8754
+ async function parseSubscriber(workPath, index, config) {
8755
+ const label = `subscriber #${index + 1}`;
8656
8756
  if (!config || typeof config !== "object" || Array.isArray(config)) {
8657
- throw subscriberError(`subscriber "${name}" must be an object`);
8757
+ throw subscriberError(`${label} must be an object`);
8658
8758
  }
8659
8759
  for (const key of Object.keys(config)) {
8660
8760
  if (!SUBSCRIBER_FIELD_NAMES.has(key)) {
8661
- throw subscriberError(
8662
- `subscriber "${name}" has unrecognized field "${key}"`
8663
- );
8761
+ throw subscriberError(`${label} has unrecognized field "${key}"`);
8664
8762
  }
8665
8763
  }
8666
8764
  if (typeof config.entrypoint !== "string") {
8667
- throw subscriberError(
8668
- `subscriber "${name}" must define string field "entrypoint"`
8669
- );
8765
+ throw subscriberError(`${label} must define string field "entrypoint"`);
8670
8766
  }
8671
- const entrypoint = parseEntrypoint(name, config.entrypoint);
8767
+ const entrypoint = parseEntrypoint(label, config.entrypoint);
8768
+ const name = getSubscriberName(entrypoint);
8672
8769
  const existingEntrypoint = await resolveExistingEntrypoint(
8673
8770
  workPath,
8674
8771
  entrypoint.filePath
@@ -8687,6 +8784,12 @@ async function parseSubscriber(workPath, name, config) {
8687
8784
  triggerDefaults: parseTriggerDefaults(name, config)
8688
8785
  };
8689
8786
  }
8787
+ function getSubscriberName({
8788
+ moduleName,
8789
+ variableName
8790
+ }) {
8791
+ return `${moduleName.replace(/\./g, "-")}_${variableName}`;
8792
+ }
8690
8793
  function parseEntrypoint(name, value) {
8691
8794
  const match = MODULE_ATTR_RE.exec(value);
8692
8795
  if (!match) {
@@ -8755,6 +8858,40 @@ function subscriberError(message) {
8755
8858
  var writeFile = import_fs15.default.promises.writeFile;
8756
8859
  var PYTHON_ENTRYPOINT_DOCS_URL2 = "https://vercel.com/docs/functions/runtimes/python#python-entrypoints";
8757
8860
  var version = -1;
8861
+ function getDevSubscriberTopics(subscriber) {
8862
+ const { retryAfterSeconds, initialDelaySeconds } = subscriber.triggerDefaults;
8863
+ return subscriber.topics.map((topic) => ({
8864
+ topic,
8865
+ ...retryAfterSeconds === void 0 ? {} : { retryAfterSeconds },
8866
+ ...initialDelaySeconds === void 0 ? {} : { initialDelaySeconds }
8867
+ }));
8868
+ }
8869
+ async function getDevSidecars({
8870
+ workPath,
8871
+ build: build2
8872
+ }) {
8873
+ const framework = build2.config?.framework;
8874
+ if (build2.config?.middleware === true || typeof framework !== "string" || !(0, import_build_utils19.isPythonFramework)(framework)) {
8875
+ return [];
8876
+ }
8877
+ const subscribers = await getPyprojectSubscribers(workPath);
8878
+ return subscribers.map((subscriber) => ({
8879
+ type: "subscriber",
8880
+ name: subscriber.name,
8881
+ consumer: getSubscriberConsumerName(subscriber.name),
8882
+ workspace: ".",
8883
+ framework,
8884
+ runtime: "python",
8885
+ builder: {
8886
+ use: build2.use,
8887
+ src: subscriber.entrypoint,
8888
+ config: {
8889
+ handlerFunction: subscriber.variableName
8890
+ }
8891
+ },
8892
+ topics: getDevSubscriberTopics(subscriber)
8893
+ }));
8894
+ }
8758
8895
  function addFiles(target, source) {
8759
8896
  for (const [p, f] of Object.entries(source)) {
8760
8897
  target[p] = f;
@@ -9052,7 +9189,6 @@ var build = async ({
9052
9189
  let spawnEnv;
9053
9190
  let projectInstallCommand;
9054
9191
  let hasCustomCommand = false;
9055
- let hasCustomBuildCommand = false;
9056
9192
  const target = getTargetPlatform(meta.isDev ?? false);
9057
9193
  (0, import_build_utils19.debug)(`workPath: ${workPath}`);
9058
9194
  workPath = await downloadFilesInWorkPath({
@@ -9085,7 +9221,8 @@ var build = async ({
9085
9221
  // For other services, handlerFunction is used as the entrypoint variable name.
9086
9222
  varName: service && (0, import_build_utils19.isScheduleTriggeredService)(service) ? void 0 : handlerFunction
9087
9223
  } : void 0,
9088
- service
9224
+ service,
9225
+ repoRootPath
9089
9226
  ) ?? void 0;
9090
9227
  if (detected?.error && detected?.baseDir === void 0) {
9091
9228
  throw detected?.error;
@@ -9258,16 +9395,12 @@ var build = async ({
9258
9395
  env: pythonEnv,
9259
9396
  cwd: workPath
9260
9397
  });
9261
- hasCustomBuildCommand = true;
9262
9398
  } else {
9263
- const ranBuildScript = await runPyprojectScript(
9399
+ await runPyprojectScript(
9264
9400
  workPath,
9265
9401
  ["vercel-build", "now-build", "build"],
9266
9402
  pythonEnv
9267
9403
  );
9268
- if (ranBuildScript) {
9269
- hasCustomBuildCommand = true;
9270
- }
9271
9404
  }
9272
9405
  });
9273
9406
  }
@@ -9371,10 +9504,12 @@ var build = async ({
9371
9504
  variableName,
9372
9505
  extraEnv: extraTrampolineEnv
9373
9506
  });
9374
- const automaticCompileAllEnabled = shouldUseCompileAll({
9507
+ const compileAllEnabled = shouldCompileAll({
9375
9508
  isDev: meta.isDev,
9376
9509
  hasCustomCommand,
9377
- hasCustomBuildCommand
9510
+ // A pre-deploy command can rewrite source after the build, which would make
9511
+ // unchecked-hash precompiled bytecode stale; skip precompilation to avoid serving it.
9512
+ hasPreDeployCommand: typeof preDeployCommand === "string"
9378
9513
  });
9379
9514
  const predefinedExcludes = [
9380
9515
  ".git/**",
@@ -9443,55 +9578,63 @@ var build = async ({
9443
9578
  });
9444
9579
  }
9445
9580
  });
9446
- const runCompileAllAndFillBytecode = async () => {
9447
- await builderSpan.child("vc.builder.python.compileall").trace(async (compileSpan) => {
9448
- const sitePackageDirs = (await getVenvSitePackagesDirs(venvPath)).filter((d) => import_fs15.default.existsSync(d));
9449
- const pythonBin = getVenvPythonBin(venvPath);
9450
- console.log("Compiling Python application bytecode...");
9451
- await runCompileAll({
9452
- pythonBin,
9453
- filesOrDirectories: [workPath],
9454
- env: pythonEnv,
9455
- excludeRegex: getCompileAllAppExcludeRegex(workPath)
9456
- });
9457
- console.log("Compiling Python dependency bytecode...");
9458
- await runCompileAll({
9459
- pythonBin,
9460
- filesOrDirectories: sitePackageDirs,
9461
- env: pythonEnv
9462
- });
9463
- compileSpan.setAttributes({
9464
- "python.compileall.enabled": "true",
9465
- "python.compileall.sitePackageDirectoryCount": String(
9466
- sitePackageDirs.length
9467
- )
9581
+ const runCompileAllAndFillBytecode = async (capacityBytes) => {
9582
+ try {
9583
+ await builderSpan.child("vc.builder.python.compileall").trace(async (compileSpan) => {
9584
+ const sitePackageDirs = (await getVenvSitePackagesDirs(venvPath)).filter((d) => import_fs15.default.existsSync(d));
9585
+ const pythonBin = getVenvPythonBin(venvPath);
9586
+ console.log("Compiling Python bytecode...");
9587
+ await runCompileAll({
9588
+ pythonBin,
9589
+ filesOrDirectories: [workPath],
9590
+ env: pythonEnv,
9591
+ excludeRegex: getCompileAllAppExcludeRegex(workPath)
9592
+ });
9593
+ await runCompileAll({
9594
+ pythonBin,
9595
+ filesOrDirectories: sitePackageDirs,
9596
+ env: pythonEnv
9597
+ });
9598
+ compileSpan.setAttributes({
9599
+ "python.compileall.enabled": "true",
9600
+ "python.compileall.sitePackageDirectoryCount": String(
9601
+ sitePackageDirs.length
9602
+ )
9603
+ });
9468
9604
  });
9469
- });
9470
- const currentSize = await calculateBundleSize(files);
9471
- let remainingCapacity = MAX_LARGE_FUNCTION_UNCOMPRESSED_SIZE - currentSize;
9472
- if (pythonVersion.major != null && pythonVersion.minor != null) {
9473
- const appBytecodeInfo = await collectAppBytecodeFiles({
9474
- workPath,
9605
+ const currentSize = await calculateBundleSize(files);
9606
+ let remainingCapacity = capacityBytes - currentSize;
9607
+ if (pythonVersion.major != null && pythonVersion.minor != null) {
9608
+ const appBytecodeInfo = await collectAppBytecodeFiles({
9609
+ workPath,
9610
+ files,
9611
+ pythonMajor: pythonVersion.major,
9612
+ pythonMinor: pythonVersion.minor
9613
+ });
9614
+ remainingCapacity = addBytecodeWithinCapacity(
9615
+ files,
9616
+ appBytecodeInfo,
9617
+ remainingCapacity
9618
+ );
9619
+ }
9620
+ const vendorBytecodeInfo = await depExternalizer.collectBytecodeFiles(
9621
+ {
9622
+ vendorDirName: vendorDir
9623
+ }
9624
+ );
9625
+ await addVendorBytecodeWithinCapacity({
9475
9626
  files,
9476
- pythonMajor: pythonVersion.major,
9477
- pythonMinor: pythonVersion.minor
9627
+ depExternalizer,
9628
+ vendorDir,
9629
+ bytecodeInfo: vendorBytecodeInfo,
9630
+ capacity: remainingCapacity
9478
9631
  });
9479
- remainingCapacity = addBytecodeWithinCapacity(
9480
- files,
9481
- appBytecodeInfo,
9482
- remainingCapacity
9632
+ } catch (err) {
9633
+ console.log(
9634
+ "Bytecode precompilation failed; continuing without precompiled bytecode."
9483
9635
  );
9636
+ (0, import_build_utils19.debug)(`bytecode precompilation error details: ${err}`);
9484
9637
  }
9485
- const vendorBytecodeInfo = await depExternalizer.collectBytecodeFiles({
9486
- vendorDirName: vendorDir
9487
- });
9488
- await addVendorBytecodeWithinCapacity({
9489
- files,
9490
- depExternalizer,
9491
- vendorDir,
9492
- bytecodeInfo: vendorBytecodeInfo,
9493
- capacity: remainingCapacity
9494
- });
9495
9638
  };
9496
9639
  const announceLargeFunction = () => console.log(
9497
9640
  `Function "${entrypoint}" exceeds the standard size limit; enabling large functions (beta).`
@@ -9500,17 +9643,29 @@ var build = async ({
9500
9643
  const { fellBackToFullBundle } = await depExternalizer.generateBundle(files);
9501
9644
  if (fellBackToFullBundle) {
9502
9645
  announceLargeFunction();
9503
- if (automaticCompileAllEnabled) {
9504
- await runCompileAllAndFillBytecode();
9646
+ if (compileAllEnabled) {
9647
+ await runCompileAllAndFillBytecode(
9648
+ MAX_LARGE_FUNCTION_UNCOMPRESSED_SIZE
9649
+ );
9505
9650
  }
9506
9651
  }
9507
9652
  } else {
9508
9653
  addFiles(files, depAnalysis.allVendorFiles);
9509
- if (isLargeFunctionsEnabled() && depAnalysis.totalBundleSize > LAMBDA_SIZE_THRESHOLD_BYTES) {
9510
- announceLargeFunction();
9511
- }
9512
- if (automaticCompileAllEnabled && depAnalysis.totalBundleSize > LAMBDA_SIZE_THRESHOLD_BYTES) {
9513
- await runCompileAllAndFillBytecode();
9654
+ if (depAnalysis.totalBundleSize > LAMBDA_SIZE_THRESHOLD_BYTES) {
9655
+ if (isLargeFunctionsEnabled()) {
9656
+ announceLargeFunction();
9657
+ }
9658
+ if (compileAllEnabled) {
9659
+ await runCompileAllAndFillBytecode(
9660
+ MAX_LARGE_FUNCTION_UNCOMPRESSED_SIZE
9661
+ );
9662
+ }
9663
+ } else if (compileAllEnabled) {
9664
+ const capacity = BYTECODE_FILL_CEILING_BYTES - depAnalysis.totalBundleSize;
9665
+ const estimate = await estimateBytecodeSize(files);
9666
+ if (capacity >= BYTECODE_COVERAGE_FLOOR * estimate) {
9667
+ await runCompileAllAndFillBytecode(BYTECODE_FILL_CEILING_BYTES);
9668
+ }
9514
9669
  }
9515
9670
  }
9516
9671
  });
@@ -9533,9 +9688,8 @@ var build = async ({
9533
9688
  });
9534
9689
  const subscriberLambdas = {};
9535
9690
  for (const subscriber of subscribers) {
9536
- const safeName = safePathSegment(subscriber.name);
9537
- const outputPath = `_py_subscribers/${safeName}`;
9538
- const consumer = (0, import_build_utils19.sanitizeConsumerName)(outputPath);
9691
+ const outputPath = getSubscriberOutputPath(subscriber.name);
9692
+ const consumer = getSubscriberConsumerName(subscriber.name);
9539
9693
  const experimentalTriggers = subscriber.topics.map(
9540
9694
  (topic) => ({
9541
9695
  type: "queue/v2beta",
@@ -9562,6 +9716,7 @@ var build = async ({
9562
9716
  environment: {
9563
9717
  ...lambdaEnv,
9564
9718
  VERCEL_HAS_WORKER_SERVICES: "1",
9719
+ // Compatibility marker consumed by the current Python runtime.
9565
9720
  VERCEL_SERVICE_TYPE: "worker"
9566
9721
  },
9567
9722
  experimentalTriggers,
@@ -9664,6 +9819,7 @@ function hasProp(obj, key) {
9664
9819
  detectEntrypoint,
9665
9820
  diagnostics,
9666
9821
  downloadFilesInWorkPath,
9822
+ getDevSidecars,
9667
9823
  installRequirement,
9668
9824
  installRequirementsFile,
9669
9825
  prepareCache,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.48.0",
3
+ "version": "6.49.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -37,7 +37,7 @@
37
37
  "vitest": "2.1.4",
38
38
  "which": "3.0.0",
39
39
  "@vercel/error-utils": "2.2.0",
40
- "@vercel/build-utils": "13.32.2",
40
+ "@vercel/build-utils": "13.32.3",
41
41
  "@vercel/python-runtime": "0.16.0"
42
42
  },
43
43
  "scripts": {