@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 +164 -24
- package/dist/index.d.ts +2 -2
- package/dist/index.js +163 -24
- package/dist/manifest/python-selector.d.ts +41 -0
- package/dist/manifest/python-specifiers.d.ts +3 -1
- package/package.json +1 -1
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
|
-
|
|
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:
|
|
1984
|
+
source: pythonVersionConfig.path,
|
|
1985
|
+
prettySource: pythonVersionConfig.path
|
|
1974
1986
|
});
|
|
1987
|
+
hasPythonVersionFile = true;
|
|
1975
1988
|
break;
|
|
1976
1989
|
}
|
|
1977
1990
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
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:
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
1906
|
+
source: pythonVersionConfig.path,
|
|
1907
|
+
prettySource: pythonVersionConfig.path
|
|
1897
1908
|
});
|
|
1909
|
+
hasPythonVersionFile = true;
|
|
1898
1910
|
break;
|
|
1899
1911
|
}
|
|
1900
1912
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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:
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
-
/**
|
|
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;
|