@vercel/python 6.16.1 → 6.17.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 CHANGED
@@ -2871,8 +2871,8 @@ var import_fs6 = __toESM(require("fs"));
2871
2871
  var import_util2 = require("util");
2872
2872
  var import_path8 = require("path");
2873
2873
 
2874
- // src/runtime-version.ts
2875
- var VERCEL_RUNTIME_VERSION = "0.5.1";
2874
+ // src/package-versions.ts
2875
+ var VERCEL_RUNTIME_VERSION = "0.5.2";
2876
2876
 
2877
2877
  // src/index.ts
2878
2878
  var import_build_utils9 = require("@vercel/build-utils");
@@ -4264,11 +4264,16 @@ var PYTHON_ENTRYPOINT_FILENAMES = [
4264
4264
  "asgi"
4265
4265
  ];
4266
4266
  var PYTHON_ENTRYPOINT_DIRS = ["", "src", "app", "api"];
4267
- var PYTHON_CANDIDATE_ENTRYPOINTS = PYTHON_ENTRYPOINT_FILENAMES.flatMap(
4268
- (filename) => PYTHON_ENTRYPOINT_DIRS.map(
4269
- (dir) => import_path6.posix.join(dir, `${filename}.py`)
4270
- )
4267
+ var PYTHON_CANDIDATE_ENTRYPOINTS = getCandidateEntrypointsInDirs(
4268
+ PYTHON_ENTRYPOINT_DIRS
4271
4269
  );
4270
+ function getCandidateEntrypointsInDirs(dirs) {
4271
+ return dirs.flatMap(
4272
+ (dir) => PYTHON_ENTRYPOINT_FILENAMES.map(
4273
+ (filename) => import_path6.posix.join(dir, `${filename}.py`)
4274
+ )
4275
+ );
4276
+ }
4272
4277
  async function getPyprojectEntrypoint(workPath) {
4273
4278
  const pyprojectData = await (0, import_build_utils7.readConfigFile)((0, import_path6.join)(workPath, "pyproject.toml"));
4274
4279
  if (!pyprojectData)
@@ -4295,6 +4300,18 @@ async function getPyprojectEntrypoint(workPath) {
4295
4300
  return null;
4296
4301
  }
4297
4302
  }
4303
+ async function findValidEntrypoint(fsFiles, candidates) {
4304
+ for (const candidate of candidates) {
4305
+ if (fsFiles[candidate]) {
4306
+ const isValid = await (0, import_build_utils6.isPythonEntrypoint)(fsFiles[candidate]);
4307
+ if (isValid) {
4308
+ (0, import_build_utils6.debug)(`Detected Python entrypoint: ${candidate}`);
4309
+ return candidate;
4310
+ }
4311
+ }
4312
+ }
4313
+ return null;
4314
+ }
4298
4315
  async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
4299
4316
  const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
4300
4317
  try {
@@ -4309,24 +4326,54 @@ async function detectGenericPythonEntrypoint(workPath, configuredEntrypoint) {
4309
4326
  const candidates = PYTHON_CANDIDATE_ENTRYPOINTS.filter(
4310
4327
  (c) => !!fsFiles[c]
4311
4328
  );
4312
- for (const candidate of candidates) {
4313
- const isValid = await (0, import_build_utils6.isPythonEntrypoint)(fsFiles[candidate]);
4329
+ return findValidEntrypoint(fsFiles, candidates);
4330
+ } catch {
4331
+ (0, import_build_utils6.debug)("Failed to discover Python entrypoint");
4332
+ return null;
4333
+ }
4334
+ }
4335
+ async function detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) {
4336
+ const entry = configuredEntrypoint.endsWith(".py") ? configuredEntrypoint : `${configuredEntrypoint}.py`;
4337
+ try {
4338
+ const fsFiles = await (0, import_build_utils6.glob)("**", workPath);
4339
+ if (fsFiles[entry]) {
4340
+ const isValid = await (0, import_build_utils6.isPythonEntrypoint)(fsFiles[entry]);
4314
4341
  if (isValid) {
4315
- (0, import_build_utils6.debug)(`Detected Python entrypoint: ${candidate}`);
4316
- return candidate;
4342
+ (0, import_build_utils6.debug)(`Using configured Python entrypoint: ${entry}`);
4343
+ return entry;
4317
4344
  }
4318
4345
  }
4319
- return null;
4346
+ const rootGlobs = await (0, import_build_utils6.glob)("*", {
4347
+ cwd: workPath,
4348
+ includeDirectories: true
4349
+ });
4350
+ const rootDirs = [
4351
+ "",
4352
+ ...Object.keys(rootGlobs).filter(
4353
+ (name) => !name.startsWith(".") && rootGlobs[name].mode != null && (0, import_build_utils6.isDirectory)(rootGlobs[name].mode)
4354
+ )
4355
+ ];
4356
+ for (const rootDir of rootDirs) {
4357
+ const currPath = (0, import_path6.join)(workPath, rootDir);
4358
+ const wsgiEntry = await (0, import_build_utils6.getDjangoEntrypoint)(currPath);
4359
+ if (wsgiEntry) {
4360
+ const fullWsgiEntry = import_path6.posix.join(rootDir, wsgiEntry);
4361
+ if (fsFiles[fullWsgiEntry]) {
4362
+ (0, import_build_utils6.debug)(`Using Django WSGI entrypoint: ${fullWsgiEntry}`);
4363
+ return fullWsgiEntry;
4364
+ }
4365
+ }
4366
+ }
4367
+ const baseCandidates = getCandidateEntrypointsInDirs(rootDirs);
4368
+ const candidates = baseCandidates.filter((c) => !!fsFiles[c]);
4369
+ return findValidEntrypoint(fsFiles, candidates);
4320
4370
  } catch {
4321
- (0, import_build_utils6.debug)("Failed to discover Python entrypoint");
4371
+ (0, import_build_utils6.debug)("Failed to discover Django Python entrypoint");
4322
4372
  return null;
4323
4373
  }
4324
4374
  }
4325
- async function detectPythonEntrypoint(_framework, workPath, configuredEntrypoint) {
4326
- const entrypoint = await detectGenericPythonEntrypoint(
4327
- workPath,
4328
- configuredEntrypoint
4329
- );
4375
+ async function detectPythonEntrypoint(framework, workPath, configuredEntrypoint) {
4376
+ const entrypoint = framework === "django" ? await detectDjangoPythonEntrypoint(workPath, configuredEntrypoint) : await detectGenericPythonEntrypoint(workPath, configuredEntrypoint);
4330
4377
  if (entrypoint)
4331
4378
  return entrypoint;
4332
4379
  return await getPyprojectEntrypoint(workPath);
@@ -4579,17 +4626,25 @@ function installGlobalCleanupHandlers() {
4579
4626
  killAll();
4580
4627
  });
4581
4628
  }
4582
- function createDevShim(workPath, modulePath) {
4629
+ function createDevShim(workPath, entry, modulePath) {
4583
4630
  try {
4584
4631
  const vercelPythonDir = (0, import_path7.join)(workPath, ".vercel", "python");
4585
4632
  (0, import_fs5.mkdirSync)(vercelPythonDir, { recursive: true });
4633
+ let qualifiedModule = modulePath;
4634
+ let extraPythonPath;
4635
+ if ((0, import_fs5.existsSync)((0, import_path7.join)(workPath, "__init__.py"))) {
4636
+ const pkgName = (0, import_path7.basename)(workPath);
4637
+ qualifiedModule = `${pkgName}.${modulePath}`;
4638
+ extraPythonPath = (0, import_path7.dirname)(workPath);
4639
+ }
4640
+ const entryAbs = (0, import_path7.join)(workPath, entry);
4586
4641
  const shimPath = (0, import_path7.join)(vercelPythonDir, `${DEV_SHIM_MODULE}.py`);
4587
4642
  const templatePath = (0, import_path7.join)(__dirname, "..", `${DEV_SHIM_MODULE}.py`);
4588
4643
  const template = (0, import_fs5.readFileSync)(templatePath, "utf8");
4589
- const shimSource = template.replace(/__VC_DEV_MODULE_PATH__/g, modulePath);
4644
+ const shimSource = template.replace(/__VC_DEV_MODULE_NAME__/g, qualifiedModule).replace(/__VC_DEV_ENTRY_ABS__/g, entryAbs);
4590
4645
  (0, import_fs5.writeFileSync)(shimPath, shimSource, "utf8");
4591
4646
  (0, import_build_utils8.debug)(`Prepared Python dev shim at ${shimPath}`);
4592
- return DEV_SHIM_MODULE;
4647
+ return { module: DEV_SHIM_MODULE, extraPythonPath };
4593
4648
  } catch (err) {
4594
4649
  (0, import_build_utils8.debug)(`Failed to prepare dev shim: ${err?.message || err}`);
4595
4650
  return null;
@@ -4753,22 +4808,33 @@ If you are using a virtual environment, activate it before running "vercel dev",
4753
4808
  }
4754
4809
  const port = typeof meta.port === "number" ? meta.port : await (0, import_get_port.default)();
4755
4810
  env.PORT = `${port}`;
4756
- const devShimModule = createDevShim(workPath, modulePath);
4757
- if (devShimModule) {
4811
+ const devShim = createDevShim(workPath, entry, modulePath);
4812
+ if (devShim) {
4758
4813
  const vercelPythonDir = (0, import_path7.join)(workPath, ".vercel", "python");
4814
+ const pathParts = [vercelPythonDir];
4815
+ if (devShim.extraPythonPath) {
4816
+ pathParts.push(devShim.extraPythonPath);
4817
+ }
4759
4818
  const existingPythonPath = env.PYTHONPATH || "";
4760
- env.PYTHONPATH = existingPythonPath ? `${vercelPythonDir}:${existingPythonPath}` : vercelPythonDir;
4819
+ if (existingPythonPath) {
4820
+ pathParts.push(existingPythonPath);
4821
+ }
4822
+ env.PYTHONPATH = pathParts.join(import_path7.delimiter);
4761
4823
  }
4762
- const moduleToRun = devShimModule || modulePath;
4824
+ const moduleToRun = devShim?.module || modulePath;
4763
4825
  const pythonArgs = ["-u", "-m", moduleToRun];
4764
4826
  const argv = [...spawnArgsPrefix, ...pythonArgs];
4765
4827
  (0, import_build_utils8.debug)(
4766
4828
  `Starting Python dev server (${framework}): ${spawnCommand} ${argv.join(" ")} [PORT=${port}]`
4767
4829
  );
4830
+ if (process.stdout.columns) {
4831
+ env.COLUMNS = `${process.stdout.columns}`;
4832
+ }
4768
4833
  const child = (0, import_child_process2.spawn)(spawnCommand, argv, {
4769
4834
  cwd: workPath,
4770
4835
  env,
4771
- stdio: ["inherit", "pipe", "pipe"]
4836
+ stdio: ["ignore", "pipe", "pipe"],
4837
+ detached: true
4772
4838
  });
4773
4839
  childProcess = child;
4774
4840
  stdoutLogListener = createLogListener(onStdout, process.stdout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python",
3
- "version": "6.16.1",
3
+ "version": "6.17.0",
4
4
  "main": "./dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -15,7 +15,7 @@
15
15
  "directory": "packages/python"
16
16
  },
17
17
  "dependencies": {
18
- "@vercel/python-analysis": "0.7.0"
18
+ "@vercel/python-analysis": "0.8.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@renovatebot/pep440": "4.2.1",
@@ -35,9 +35,9 @@
35
35
  "which": "3.0.0",
36
36
  "get-port": "5.1.1",
37
37
  "is-port-reachable": "3.1.0",
38
- "@vercel/python-runtime": "0.5.1",
39
- "@vercel/build-utils": "13.5.0",
40
- "@vercel/error-utils": "2.0.3"
38
+ "@vercel/build-utils": "13.6.0",
39
+ "@vercel/error-utils": "2.0.3",
40
+ "@vercel/python-runtime": "0.5.2"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "node ../../utils/build-builder.mjs",
package/vc_init_dev.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  import os
6
6
  import inspect
7
7
  from os import path as _p
8
- from importlib import import_module
8
+ from importlib import util as _importlib_util
9
9
  import mimetypes
10
10
 
11
11
 
@@ -72,8 +72,20 @@ def _strip_service_route_prefix(path_value):
72
72
 
73
73
 
74
74
  # ASGI/WSGI app detection
75
- USER_MODULE = "__VC_DEV_MODULE_PATH__"
76
- _mod = import_module(USER_MODULE)
75
+ _MODULE_NAME = "__VC_DEV_MODULE_NAME__"
76
+ _ENTRY_ABS = "__VC_DEV_ENTRY_ABS__"
77
+
78
+ # Import user module by file path, matching vc_init.py's approach.
79
+ # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
80
+ _spec = _importlib_util.spec_from_file_location(_MODULE_NAME, _ENTRY_ABS)
81
+ if _spec is None or _spec.loader is None:
82
+ raise RuntimeError(
83
+ f"Could not load module spec for '{_MODULE_NAME}' at {_ENTRY_ABS}"
84
+ )
85
+ _mod = _importlib_util.module_from_spec(_spec)
86
+ sys.modules[_MODULE_NAME] = _mod
87
+ _spec.loader.exec_module(_mod)
88
+
77
89
  _user_app_name = (
78
90
  "app"
79
91
  if hasattr(_mod, "app")
@@ -83,7 +95,7 @@ _user_app_name = (
83
95
  )
84
96
  if _user_app_name is None:
85
97
  raise RuntimeError(
86
- f"Missing 'app' or 'application' in module '{USER_MODULE}'. "
98
+ f"Missing 'app' or 'application' in module '{_MODULE_NAME}'. "
87
99
  f"Define `app = ...` or `application = ...` in your entrypoint."
88
100
  )
89
101
 
@@ -137,7 +149,7 @@ def _detect_app_type(app_obj):
137
149
 
138
150
  print(
139
151
  _color(
140
- f"Could not determine the application interface for '{USER_MODULE}:{_user_app_name}'\n"
152
+ f"Could not determine the application interface for '{_MODULE_NAME}:{_user_app_name}'\n"
141
153
  f"Expected either:\n"
142
154
  f" - An ASGI app: async callable(scope, receive, send)\n"
143
155
  f" - A WSGI app: callable(environ, start_response)",