@vercel/python-analysis 0.8.1 → 0.8.2

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.cjs CHANGED
@@ -74,6 +74,7 @@ __export(src_exports, {
74
74
  parseUvLock: () => parseUvLock,
75
75
  scanDistributions: () => scanDistributions,
76
76
  selectPython: () => selectPython,
77
+ selectPythonVersion: () => selectPythonVersion,
77
78
  stringifyManifest: () => stringifyManifest
78
79
  });
79
80
  module.exports = __toCommonJS(src_exports);
@@ -739,17 +740,24 @@ function convertPipfileToPyprojectToml(pipfile) {
739
740
  function convertPipfileLockToPyprojectToml(pipfileLock) {
740
741
  const sources = {};
741
742
  const pyproject = {};
743
+ const pythonVersion = pipfileLock._meta?.requires?.python_version;
742
744
  const deps = [];
743
745
  for (const dep of pipfileLockDepsToRequirements(pipfileLock.default || {})) {
744
746
  deps.push(formatPep508(dep));
745
747
  addDepSource(sources, dep);
746
748
  }
747
- if (deps.length > 0) {
748
- pyproject.project = {
749
+ if (deps.length > 0 || pythonVersion) {
750
+ const project = {
749
751
  name: "app",
750
- version: "0.1.0",
751
- dependencies: deps
752
+ version: "0.1.0"
752
753
  };
754
+ if (pythonVersion) {
755
+ project["requires-python"] = `==${pythonVersion}.*`;
756
+ }
757
+ if (deps.length > 0) {
758
+ project.dependencies = deps;
759
+ }
760
+ pyproject.project = project;
753
761
  }
754
762
  const devDeps = [];
755
763
  for (const dep of pipfileLockDepsToRequirements(pipfileLock.develop || {})) {
@@ -1930,8 +1938,10 @@ async function discoverPythonPackage({
1930
1938
  let workspaceManifest;
1931
1939
  let workspaceLockFile;
1932
1940
  if (manifests.length === 0) {
1941
+ const requiresPython2 = computeRequiresPython(void 0, void 0, configs);
1933
1942
  return {
1934
- configs
1943
+ configs,
1944
+ requiresPython: requiresPython2
1935
1945
  };
1936
1946
  } else {
1937
1947
  entrypointManifest = manifests[0];
@@ -1965,37 +1975,44 @@ async function discoverPythonPackage({
1965
1975
  }
1966
1976
  function computeRequiresPython(manifest, workspaceManifest, configs) {
1967
1977
  const constraints = [];
1978
+ let hasPythonVersionFile = false;
1968
1979
  for (const configSet of configs) {
1969
1980
  const pythonVersionConfig = configSet[".python-version" /* PythonVersion */];
1970
1981
  if (pythonVersionConfig !== void 0) {
1971
1982
  constraints.push({
1972
1983
  request: pythonVersionConfig.data,
1973
- source: `${pythonVersionConfig.path}`
1984
+ source: pythonVersionConfig.path,
1985
+ prettySource: pythonVersionConfig.path
1974
1986
  });
1987
+ hasPythonVersionFile = true;
1975
1988
  break;
1976
1989
  }
1977
1990
  }
1978
- const manifestRequiresPython = manifest?.data.project?.["requires-python"];
1979
- if (manifestRequiresPython) {
1980
- const parsed = (0, import_specifier.parse)(manifestRequiresPython);
1981
- if (parsed?.length) {
1982
- const request = pythonRequestFromConstraint(parsed);
1983
- constraints.push({
1984
- request: [request],
1985
- source: `"requires-python" key in ${manifest.path}`
1986
- });
1987
- }
1988
- } else {
1989
- const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
1990
- if (workspaceRequiresPython) {
1991
- const parsed = (0, import_specifier.parse)(workspaceRequiresPython);
1991
+ if (!hasPythonVersionFile) {
1992
+ const manifestRequiresPython = manifest?.data.project?.["requires-python"];
1993
+ if (manifestRequiresPython) {
1994
+ const parsed = (0, import_specifier.parse)(manifestRequiresPython);
1992
1995
  if (parsed?.length) {
1993
1996
  const request = pythonRequestFromConstraint(parsed);
1994
1997
  constraints.push({
1995
1998
  request: [request],
1996
- source: `"requires-python" key in ${workspaceManifest.path}`
1999
+ source: manifest.path,
2000
+ prettySource: `"requires-python" key in ${manifest.path}`
1997
2001
  });
1998
2002
  }
2003
+ } else {
2004
+ const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
2005
+ if (workspaceRequiresPython) {
2006
+ const parsed = (0, import_specifier.parse)(workspaceRequiresPython);
2007
+ if (parsed?.length) {
2008
+ const request = pythonRequestFromConstraint(parsed);
2009
+ constraints.push({
2010
+ request: [request],
2011
+ source: workspaceManifest.path,
2012
+ prettySource: `"requires-python" key in ${workspaceManifest.path}`
2013
+ });
2014
+ }
2015
+ }
1999
2016
  }
2000
2017
  }
2001
2018
  return constraints;
@@ -2407,13 +2424,15 @@ function selectPython(constraints, available) {
2407
2424
  }
2408
2425
  }
2409
2426
  if (constraintsWithMatches.length > 1) {
2410
- const sources = constraintsWithMatches.map((i) => constraints[i].source);
2427
+ const sources = constraintsWithMatches.map(
2428
+ (i) => constraints[i].prettySource
2429
+ );
2411
2430
  warnings.push(
2412
2431
  `Python version constraints may not overlap: ${sources.join(", ")}`
2413
2432
  );
2414
2433
  }
2415
2434
  }
2416
- const constraintDescriptions = constraints.map((c) => c.source).join(", ");
2435
+ const constraintDescriptions = constraints.map((c) => c.prettySource).join(", ");
2417
2436
  errors.push(
2418
2437
  `No Python build satisfies all constraints: ${constraintDescriptions}`
2419
2438
  );
@@ -2423,6 +2442,126 @@ function selectPython(constraints, available) {
2423
2442
  warnings: warnings.length > 0 ? warnings : void 0
2424
2443
  };
2425
2444
  }
2445
+ function selectPythonVersion({
2446
+ constraints,
2447
+ availableBuilds,
2448
+ allBuilds,
2449
+ defaultBuild,
2450
+ majorMinorOnly,
2451
+ legacyTildeEquals
2452
+ }) {
2453
+ const source = constraints?.[0]?.source;
2454
+ if (!constraints || constraints.length === 0) {
2455
+ return { build: defaultBuild };
2456
+ }
2457
+ let effectiveConstraints = majorMinorOnly ? constraints.map((c) => ({
2458
+ ...c,
2459
+ request: c.request.map(truncatePatchVersionsInRequest)
2460
+ })) : constraints;
2461
+ if (legacyTildeEquals) {
2462
+ effectiveConstraints = effectiveConstraints.map((c) => ({
2463
+ ...c,
2464
+ request: c.request.map(legacyTildeEqualsTransform)
2465
+ }));
2466
+ }
2467
+ const result = selectPython(effectiveConstraints, availableBuilds);
2468
+ if (result.build) {
2469
+ return { build: result.build, source };
2470
+ }
2471
+ const allResult = selectPython(effectiveConstraints, allBuilds);
2472
+ if (allResult.build) {
2473
+ const version = pythonVersionToString(allResult.build.version);
2474
+ return {
2475
+ build: defaultBuild,
2476
+ source,
2477
+ notAvailable: { build: allResult.build, version }
2478
+ };
2479
+ }
2480
+ const versionString = extractConstraintVersionString(constraints);
2481
+ return {
2482
+ build: defaultBuild,
2483
+ source,
2484
+ invalidConstraint: { versionString }
2485
+ };
2486
+ }
2487
+ function extractConstraintVersionString(constraints) {
2488
+ for (const c of constraints) {
2489
+ for (const req of c.request) {
2490
+ if (req.version?.constraint && req.version.constraint.length > 0) {
2491
+ const specs = req.version.constraint;
2492
+ if (specs.length === 1 && specs[0].operator === "==" && (!specs[0].prefix || specs[0].prefix === ".*")) {
2493
+ return specs[0].version;
2494
+ }
2495
+ return pep440ConstraintsToString(specs);
2496
+ }
2497
+ }
2498
+ }
2499
+ return "unknown";
2500
+ }
2501
+ function truncatePatchVersionsInRequest(req) {
2502
+ if (!req.version?.constraint || req.version.constraint.length === 0) {
2503
+ return req;
2504
+ }
2505
+ const newConstraints = [];
2506
+ for (const c of req.version.constraint) {
2507
+ const result = truncatePatchConstraint(c);
2508
+ if (result !== null) {
2509
+ newConstraints.push(result);
2510
+ }
2511
+ }
2512
+ const newVersion = {
2513
+ ...req.version,
2514
+ constraint: newConstraints
2515
+ };
2516
+ return { ...req, version: newVersion };
2517
+ }
2518
+ function truncatePatchConstraint(c) {
2519
+ if (c.operator === "===") {
2520
+ return c;
2521
+ }
2522
+ const parts = c.version.split(".");
2523
+ if (parts.length < 3) {
2524
+ return c;
2525
+ }
2526
+ const majorMinor = parts.slice(0, 2).join(".");
2527
+ const patch = parseInt(parts[2], 10);
2528
+ switch (c.operator) {
2529
+ case "==":
2530
+ return { operator: "==", version: majorMinor, prefix: ".*" };
2531
+ case "!=":
2532
+ return null;
2533
+ case "~=":
2534
+ return { operator: "==", version: majorMinor, prefix: ".*" };
2535
+ case ">=":
2536
+ return { operator: ">=", version: majorMinor, prefix: "" };
2537
+ case "<=":
2538
+ return { operator: "<=", version: majorMinor, prefix: "" };
2539
+ case ">":
2540
+ return patch === 0 ? { operator: ">", version: majorMinor, prefix: "" } : { operator: ">=", version: majorMinor, prefix: "" };
2541
+ case "<":
2542
+ return patch === 0 ? { operator: "<", version: majorMinor, prefix: "" } : { operator: "<=", version: majorMinor, prefix: "" };
2543
+ default:
2544
+ return c;
2545
+ }
2546
+ }
2547
+ function legacyTildeEqualsTransform(req) {
2548
+ if (!req.version?.constraint || req.version.constraint.length === 0) {
2549
+ return req;
2550
+ }
2551
+ const newConstraints = req.version.constraint.map((c) => {
2552
+ if (c.operator !== "~=")
2553
+ return c;
2554
+ const parts = c.version.split(".");
2555
+ if (parts.length === 2) {
2556
+ return { operator: "==", version: c.version, prefix: ".*" };
2557
+ }
2558
+ return c;
2559
+ });
2560
+ return {
2561
+ ...req,
2562
+ version: { ...req.version, constraint: newConstraints }
2563
+ };
2564
+ }
2426
2565
  function pythonVersionToString(version) {
2427
2566
  let str = `${version.major}.${version.minor}`;
2428
2567
  if (version.patch !== void 0) {
@@ -2434,7 +2573,7 @@ function pythonVersionToString(version) {
2434
2573
  return str;
2435
2574
  }
2436
2575
  function pep440ConstraintsToString(constraints) {
2437
- return constraints.map((c) => `${c.operator}${c.prefix}${c.version}`).join(",");
2576
+ return constraints.map((c) => `${c.operator}${c.version}${c.prefix ?? ""}`).join(",");
2438
2577
  }
2439
2578
  function implementationsMatch(buildImpl, requestImpl) {
2440
2579
  if (PythonImplementation.isUnknown(buildImpl)) {
@@ -2562,5 +2701,6 @@ var HashDigestSchema = hashDigestSchema;
2562
2701
  parseUvLock,
2563
2702
  scanDistributions,
2564
2703
  selectPython,
2704
+ selectPythonVersion,
2565
2705
  stringifyManifest
2566
2706
  });
package/dist/index.d.ts CHANGED
@@ -14,8 +14,8 @@ export { discoverPythonPackage, PythonConfigKind, PythonLockFileKind, PythonMani
14
14
  export { createMinimalManifest, stringifyManifest, type CreateMinimalManifestOptions, } from './manifest/serialize';
15
15
  export type { ClassifyPackagesOptions, PackageClassification, UvLockFile, UvLockPackage, UvLockPackageSource, } from './manifest/uv-lock-parser';
16
16
  export { classifyPackages, isPrivatePackageSource, normalizePackageName, parseUvLock, } from './manifest/uv-lock-parser';
17
- export type { PythonSelectionResult } from './manifest/python-selector';
18
- export { selectPython } from './manifest/python-selector';
17
+ export type { PythonSelectionResult, PythonVersionSelectionResult, } from './manifest/python-selector';
18
+ export { selectPython, selectPythonVersion } from './manifest/python-selector';
19
19
  export { PythonAnalysisError } from './util/error';
20
20
  export { LicenseObjectSchema, LicenseSchema, PersonSchema, PyProjectBuildSystemSchema, PyProjectDependencyGroupsSchema, PyProjectProjectSchema, PyProjectToolSectionSchema, PyProjectTomlSchema, ReadmeObjectSchema, ReadmeSchema, } from './manifest/pyproject/schema';
21
21
  export type { License, LicenseObject, Person, PyProjectBuildSystem, PyProjectDependencyGroups, PyProjectProject, PyProjectToml, PyProjectToolSection, Readme, ReadmeObject, } from './manifest/pyproject/types';
package/dist/index.js CHANGED
@@ -662,17 +662,24 @@ function convertPipfileToPyprojectToml(pipfile) {
662
662
  function convertPipfileLockToPyprojectToml(pipfileLock) {
663
663
  const sources = {};
664
664
  const pyproject = {};
665
+ const pythonVersion = pipfileLock._meta?.requires?.python_version;
665
666
  const deps = [];
666
667
  for (const dep of pipfileLockDepsToRequirements(pipfileLock.default || {})) {
667
668
  deps.push(formatPep508(dep));
668
669
  addDepSource(sources, dep);
669
670
  }
670
- if (deps.length > 0) {
671
- pyproject.project = {
671
+ if (deps.length > 0 || pythonVersion) {
672
+ const project = {
672
673
  name: "app",
673
- version: "0.1.0",
674
- dependencies: deps
674
+ version: "0.1.0"
675
675
  };
676
+ if (pythonVersion) {
677
+ project["requires-python"] = `==${pythonVersion}.*`;
678
+ }
679
+ if (deps.length > 0) {
680
+ project.dependencies = deps;
681
+ }
682
+ pyproject.project = project;
676
683
  }
677
684
  const devDeps = [];
678
685
  for (const dep of pipfileLockDepsToRequirements(pipfileLock.develop || {})) {
@@ -1853,8 +1860,10 @@ async function discoverPythonPackage({
1853
1860
  let workspaceManifest;
1854
1861
  let workspaceLockFile;
1855
1862
  if (manifests.length === 0) {
1863
+ const requiresPython2 = computeRequiresPython(void 0, void 0, configs);
1856
1864
  return {
1857
- configs
1865
+ configs,
1866
+ requiresPython: requiresPython2
1858
1867
  };
1859
1868
  } else {
1860
1869
  entrypointManifest = manifests[0];
@@ -1888,37 +1897,44 @@ async function discoverPythonPackage({
1888
1897
  }
1889
1898
  function computeRequiresPython(manifest, workspaceManifest, configs) {
1890
1899
  const constraints = [];
1900
+ let hasPythonVersionFile = false;
1891
1901
  for (const configSet of configs) {
1892
1902
  const pythonVersionConfig = configSet[".python-version" /* PythonVersion */];
1893
1903
  if (pythonVersionConfig !== void 0) {
1894
1904
  constraints.push({
1895
1905
  request: pythonVersionConfig.data,
1896
- source: `${pythonVersionConfig.path}`
1906
+ source: pythonVersionConfig.path,
1907
+ prettySource: pythonVersionConfig.path
1897
1908
  });
1909
+ hasPythonVersionFile = true;
1898
1910
  break;
1899
1911
  }
1900
1912
  }
1901
- const manifestRequiresPython = manifest?.data.project?.["requires-python"];
1902
- if (manifestRequiresPython) {
1903
- const parsed = parse2(manifestRequiresPython);
1904
- if (parsed?.length) {
1905
- const request = pythonRequestFromConstraint(parsed);
1906
- constraints.push({
1907
- request: [request],
1908
- source: `"requires-python" key in ${manifest.path}`
1909
- });
1910
- }
1911
- } else {
1912
- const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
1913
- if (workspaceRequiresPython) {
1914
- const parsed = parse2(workspaceRequiresPython);
1913
+ if (!hasPythonVersionFile) {
1914
+ const manifestRequiresPython = manifest?.data.project?.["requires-python"];
1915
+ if (manifestRequiresPython) {
1916
+ const parsed = parse2(manifestRequiresPython);
1915
1917
  if (parsed?.length) {
1916
1918
  const request = pythonRequestFromConstraint(parsed);
1917
1919
  constraints.push({
1918
1920
  request: [request],
1919
- source: `"requires-python" key in ${workspaceManifest.path}`
1921
+ source: manifest.path,
1922
+ prettySource: `"requires-python" key in ${manifest.path}`
1920
1923
  });
1921
1924
  }
1925
+ } else {
1926
+ const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
1927
+ if (workspaceRequiresPython) {
1928
+ const parsed = parse2(workspaceRequiresPython);
1929
+ if (parsed?.length) {
1930
+ const request = pythonRequestFromConstraint(parsed);
1931
+ constraints.push({
1932
+ request: [request],
1933
+ source: workspaceManifest.path,
1934
+ prettySource: `"requires-python" key in ${workspaceManifest.path}`
1935
+ });
1936
+ }
1937
+ }
1922
1938
  }
1923
1939
  }
1924
1940
  return constraints;
@@ -2330,13 +2346,15 @@ function selectPython(constraints, available) {
2330
2346
  }
2331
2347
  }
2332
2348
  if (constraintsWithMatches.length > 1) {
2333
- const sources = constraintsWithMatches.map((i) => constraints[i].source);
2349
+ const sources = constraintsWithMatches.map(
2350
+ (i) => constraints[i].prettySource
2351
+ );
2334
2352
  warnings.push(
2335
2353
  `Python version constraints may not overlap: ${sources.join(", ")}`
2336
2354
  );
2337
2355
  }
2338
2356
  }
2339
- const constraintDescriptions = constraints.map((c) => c.source).join(", ");
2357
+ const constraintDescriptions = constraints.map((c) => c.prettySource).join(", ");
2340
2358
  errors.push(
2341
2359
  `No Python build satisfies all constraints: ${constraintDescriptions}`
2342
2360
  );
@@ -2346,6 +2364,126 @@ function selectPython(constraints, available) {
2346
2364
  warnings: warnings.length > 0 ? warnings : void 0
2347
2365
  };
2348
2366
  }
2367
+ function selectPythonVersion({
2368
+ constraints,
2369
+ availableBuilds,
2370
+ allBuilds,
2371
+ defaultBuild,
2372
+ majorMinorOnly,
2373
+ legacyTildeEquals
2374
+ }) {
2375
+ const source = constraints?.[0]?.source;
2376
+ if (!constraints || constraints.length === 0) {
2377
+ return { build: defaultBuild };
2378
+ }
2379
+ let effectiveConstraints = majorMinorOnly ? constraints.map((c) => ({
2380
+ ...c,
2381
+ request: c.request.map(truncatePatchVersionsInRequest)
2382
+ })) : constraints;
2383
+ if (legacyTildeEquals) {
2384
+ effectiveConstraints = effectiveConstraints.map((c) => ({
2385
+ ...c,
2386
+ request: c.request.map(legacyTildeEqualsTransform)
2387
+ }));
2388
+ }
2389
+ const result = selectPython(effectiveConstraints, availableBuilds);
2390
+ if (result.build) {
2391
+ return { build: result.build, source };
2392
+ }
2393
+ const allResult = selectPython(effectiveConstraints, allBuilds);
2394
+ if (allResult.build) {
2395
+ const version = pythonVersionToString(allResult.build.version);
2396
+ return {
2397
+ build: defaultBuild,
2398
+ source,
2399
+ notAvailable: { build: allResult.build, version }
2400
+ };
2401
+ }
2402
+ const versionString = extractConstraintVersionString(constraints);
2403
+ return {
2404
+ build: defaultBuild,
2405
+ source,
2406
+ invalidConstraint: { versionString }
2407
+ };
2408
+ }
2409
+ function extractConstraintVersionString(constraints) {
2410
+ for (const c of constraints) {
2411
+ for (const req of c.request) {
2412
+ if (req.version?.constraint && req.version.constraint.length > 0) {
2413
+ const specs = req.version.constraint;
2414
+ if (specs.length === 1 && specs[0].operator === "==" && (!specs[0].prefix || specs[0].prefix === ".*")) {
2415
+ return specs[0].version;
2416
+ }
2417
+ return pep440ConstraintsToString(specs);
2418
+ }
2419
+ }
2420
+ }
2421
+ return "unknown";
2422
+ }
2423
+ function truncatePatchVersionsInRequest(req) {
2424
+ if (!req.version?.constraint || req.version.constraint.length === 0) {
2425
+ return req;
2426
+ }
2427
+ const newConstraints = [];
2428
+ for (const c of req.version.constraint) {
2429
+ const result = truncatePatchConstraint(c);
2430
+ if (result !== null) {
2431
+ newConstraints.push(result);
2432
+ }
2433
+ }
2434
+ const newVersion = {
2435
+ ...req.version,
2436
+ constraint: newConstraints
2437
+ };
2438
+ return { ...req, version: newVersion };
2439
+ }
2440
+ function truncatePatchConstraint(c) {
2441
+ if (c.operator === "===") {
2442
+ return c;
2443
+ }
2444
+ const parts = c.version.split(".");
2445
+ if (parts.length < 3) {
2446
+ return c;
2447
+ }
2448
+ const majorMinor = parts.slice(0, 2).join(".");
2449
+ const patch = parseInt(parts[2], 10);
2450
+ switch (c.operator) {
2451
+ case "==":
2452
+ return { operator: "==", version: majorMinor, prefix: ".*" };
2453
+ case "!=":
2454
+ return null;
2455
+ case "~=":
2456
+ return { operator: "==", version: majorMinor, prefix: ".*" };
2457
+ case ">=":
2458
+ return { operator: ">=", version: majorMinor, prefix: "" };
2459
+ case "<=":
2460
+ return { operator: "<=", version: majorMinor, prefix: "" };
2461
+ case ">":
2462
+ return patch === 0 ? { operator: ">", version: majorMinor, prefix: "" } : { operator: ">=", version: majorMinor, prefix: "" };
2463
+ case "<":
2464
+ return patch === 0 ? { operator: "<", version: majorMinor, prefix: "" } : { operator: "<=", version: majorMinor, prefix: "" };
2465
+ default:
2466
+ return c;
2467
+ }
2468
+ }
2469
+ function legacyTildeEqualsTransform(req) {
2470
+ if (!req.version?.constraint || req.version.constraint.length === 0) {
2471
+ return req;
2472
+ }
2473
+ const newConstraints = req.version.constraint.map((c) => {
2474
+ if (c.operator !== "~=")
2475
+ return c;
2476
+ const parts = c.version.split(".");
2477
+ if (parts.length === 2) {
2478
+ return { operator: "==", version: c.version, prefix: ".*" };
2479
+ }
2480
+ return c;
2481
+ });
2482
+ return {
2483
+ ...req,
2484
+ version: { ...req.version, constraint: newConstraints }
2485
+ };
2486
+ }
2349
2487
  function pythonVersionToString(version) {
2350
2488
  let str = `${version.major}.${version.minor}`;
2351
2489
  if (version.patch !== void 0) {
@@ -2357,7 +2495,7 @@ function pythonVersionToString(version) {
2357
2495
  return str;
2358
2496
  }
2359
2497
  function pep440ConstraintsToString(constraints) {
2360
- return constraints.map((c) => `${c.operator}${c.prefix}${c.version}`).join(",");
2498
+ return constraints.map((c) => `${c.operator}${c.version}${c.prefix ?? ""}`).join(",");
2361
2499
  }
2362
2500
  function implementationsMatch(buildImpl, requestImpl) {
2363
2501
  if (PythonImplementation.isUnknown(buildImpl)) {
@@ -2484,5 +2622,6 @@ export {
2484
2622
  parseUvLock,
2485
2623
  scanDistributions,
2486
2624
  selectPython,
2625
+ selectPythonVersion,
2487
2626
  stringifyManifest
2488
2627
  };
@@ -20,6 +20,47 @@ export interface PythonSelectionResult {
20
20
  * @returns The first matching build, or null with errors/warnings if none match
21
21
  */
22
22
  export declare function selectPython(constraints: PythonConstraint[], available: PythonBuild[]): PythonSelectionResult;
23
+ /**
24
+ * Result of the higher-level selectPythonVersion function.
25
+ */
26
+ export interface PythonVersionSelectionResult {
27
+ /** The selected build. Falls back to defaultBuild if no constraints match. */
28
+ build: PythonBuild;
29
+ /** Source file where the constraint originated (e.g. "pyproject.toml"). */
30
+ source?: string;
31
+ /** Diagnostic indicating the constraint was found but the build isn't in availableBuilds. */
32
+ notAvailable?: {
33
+ build: PythonBuild;
34
+ /** The version string of the unavailable build. */
35
+ version: string;
36
+ };
37
+ /** Diagnostic indicating no build matches the constraint at all. */
38
+ invalidConstraint?: {
39
+ /** Human-readable version string from the constraint. */
40
+ versionString: string;
41
+ };
42
+ }
43
+ /**
44
+ * Higher-level Python version selection with two-pass matching and diagnostics.
45
+ *
46
+ * First tries to match constraints against availableBuilds. If no match,
47
+ * tries against allBuilds to produce diagnostic information. Falls back
48
+ * to defaultBuild if no constraints are provided or no match is found.
49
+ */
50
+ export declare function selectPythonVersion({ constraints, availableBuilds, allBuilds, defaultBuild, majorMinorOnly, legacyTildeEquals, }: {
51
+ constraints?: PythonConstraint[];
52
+ availableBuilds: PythonBuild[];
53
+ allBuilds: PythonBuild[];
54
+ defaultBuild: PythonBuild;
55
+ majorMinorOnly?: boolean;
56
+ /**
57
+ * When true, treat 2-part compatible-release specifiers (`~=X.Y`) as
58
+ * pinning to exactly that minor version (`==X.Y.*`) rather than the
59
+ * PEP 440 correct `>=X.Y, <(X+1).0`. This preserves the historical
60
+ * behaviour of the Python builder prior to the python-analysis migration.
61
+ */
62
+ legacyTildeEquals?: boolean;
63
+ }): PythonVersionSelectionResult;
23
64
  /**
24
65
  * Convert a PythonVersion to a string suitable for PEP 440 comparison.
25
66
  *
@@ -51,8 +51,10 @@ export interface PythonVersionRequest {
51
51
  export interface PythonConstraint {
52
52
  /** The Python version request(s) that define this constraint. */
53
53
  request: PythonRequest[];
54
- /** Human-readable description of where this constraint came from. */
54
+ /** Config file name where this constraint originated (e.g. "pyproject.toml", ".python-version"). */
55
55
  source: string;
56
+ /** Human-readable description of where this constraint came from. */
57
+ prettySource: string;
56
58
  }
57
59
  export type PythonVersion = {
58
60
  major: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/python-analysis",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",