@vercel/python-analysis 0.8.1 → 0.9.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.cjs +173 -34
- package/dist/index.d.ts +3 -3
- package/dist/index.js +171 -33
- package/dist/manifest/python-selector.d.ts +41 -0
- package/dist/manifest/python-specifiers.d.ts +3 -1
- package/dist/semantic/entrypoints.d.ts +10 -9
- package/dist/wasm/interfaces/wasi-cli-stdin.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-cli-stdout.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-cli-terminal-input.d.ts +8 -0
- package/dist/wasm/interfaces/wasi-cli-terminal-output.d.ts +8 -0
- package/dist/wasm/interfaces/wasi-cli-terminal-stderr.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-cli-terminal-stdin.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-cli-terminal-stdout.d.ts +3 -0
- package/dist/wasm/interfaces/wasi-io-error.d.ts +0 -1
- package/dist/wasm/interfaces/wasi-io-poll.d.ts +9 -0
- package/dist/wasm/interfaces/wasi-io-streams.d.ts +12 -1
- package/dist/wasm/vercel_python_analysis.core.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.core2.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.core3.wasm +0 -0
- package/dist/wasm/vercel_python_analysis.d.ts +17 -1
- package/dist/wasm/vercel_python_analysis.js +542 -157
- package/package.json +1 -1
- package/dist/wasm/vercel_python_analysis.core4.wasm +0 -0
package/dist/index.cjs
CHANGED
|
@@ -64,16 +64,17 @@ __export(src_exports, {
|
|
|
64
64
|
UvIndexEntrySchema: () => UvIndexEntrySchema,
|
|
65
65
|
classifyPackages: () => classifyPackages,
|
|
66
66
|
containsAppOrHandler: () => containsAppOrHandler,
|
|
67
|
+
containsTopLevelCallable: () => containsTopLevelCallable,
|
|
67
68
|
createMinimalManifest: () => createMinimalManifest,
|
|
68
69
|
discoverPythonPackage: () => discoverPythonPackage,
|
|
69
70
|
extendDistRecord: () => extendDistRecord,
|
|
70
71
|
getStringConstant: () => getStringConstant,
|
|
71
72
|
isPrivatePackageSource: () => isPrivatePackageSource,
|
|
72
73
|
normalizePackageName: () => normalizePackageName2,
|
|
73
|
-
parseDjangoSettingsModule: () => parseDjangoSettingsModule,
|
|
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);
|
|
@@ -126,17 +127,16 @@ async function containsAppOrHandler(source) {
|
|
|
126
127
|
const mod = await importWasmModule();
|
|
127
128
|
return mod.containsAppOrHandler(source);
|
|
128
129
|
}
|
|
129
|
-
async function
|
|
130
|
+
async function containsTopLevelCallable(source, name) {
|
|
131
|
+
if (!source.includes(name)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
130
134
|
const mod = await importWasmModule();
|
|
131
|
-
return mod.
|
|
135
|
+
return mod.containsTopLevelCallable(source, name);
|
|
132
136
|
}
|
|
133
|
-
|
|
134
|
-
async function parseDjangoSettingsModule(content) {
|
|
135
|
-
if (!DJANGO_SETTINGS_MODULE_PATTERN_RE.test(content)) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
137
|
+
async function getStringConstant(source, name) {
|
|
138
138
|
const mod = await importWasmModule();
|
|
139
|
-
return mod.
|
|
139
|
+
return mod.getStringConstant(source, name) ?? null;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// src/manifest/dist-metadata.ts
|
|
@@ -739,17 +739,24 @@ function convertPipfileToPyprojectToml(pipfile) {
|
|
|
739
739
|
function convertPipfileLockToPyprojectToml(pipfileLock) {
|
|
740
740
|
const sources = {};
|
|
741
741
|
const pyproject = {};
|
|
742
|
+
const pythonVersion = pipfileLock._meta?.requires?.python_version;
|
|
742
743
|
const deps = [];
|
|
743
744
|
for (const dep of pipfileLockDepsToRequirements(pipfileLock.default || {})) {
|
|
744
745
|
deps.push(formatPep508(dep));
|
|
745
746
|
addDepSource(sources, dep);
|
|
746
747
|
}
|
|
747
|
-
if (deps.length > 0) {
|
|
748
|
-
|
|
748
|
+
if (deps.length > 0 || pythonVersion) {
|
|
749
|
+
const project = {
|
|
749
750
|
name: "app",
|
|
750
|
-
version: "0.1.0"
|
|
751
|
-
dependencies: deps
|
|
751
|
+
version: "0.1.0"
|
|
752
752
|
};
|
|
753
|
+
if (pythonVersion) {
|
|
754
|
+
project["requires-python"] = `==${pythonVersion}.*`;
|
|
755
|
+
}
|
|
756
|
+
if (deps.length > 0) {
|
|
757
|
+
project.dependencies = deps;
|
|
758
|
+
}
|
|
759
|
+
pyproject.project = project;
|
|
753
760
|
}
|
|
754
761
|
const devDeps = [];
|
|
755
762
|
for (const dep of pipfileLockDepsToRequirements(pipfileLock.develop || {})) {
|
|
@@ -1930,8 +1937,10 @@ async function discoverPythonPackage({
|
|
|
1930
1937
|
let workspaceManifest;
|
|
1931
1938
|
let workspaceLockFile;
|
|
1932
1939
|
if (manifests.length === 0) {
|
|
1940
|
+
const requiresPython2 = computeRequiresPython(void 0, void 0, configs);
|
|
1933
1941
|
return {
|
|
1934
|
-
configs
|
|
1942
|
+
configs,
|
|
1943
|
+
requiresPython: requiresPython2
|
|
1935
1944
|
};
|
|
1936
1945
|
} else {
|
|
1937
1946
|
entrypointManifest = manifests[0];
|
|
@@ -1965,37 +1974,44 @@ async function discoverPythonPackage({
|
|
|
1965
1974
|
}
|
|
1966
1975
|
function computeRequiresPython(manifest, workspaceManifest, configs) {
|
|
1967
1976
|
const constraints = [];
|
|
1977
|
+
let hasPythonVersionFile = false;
|
|
1968
1978
|
for (const configSet of configs) {
|
|
1969
1979
|
const pythonVersionConfig = configSet[".python-version" /* PythonVersion */];
|
|
1970
1980
|
if (pythonVersionConfig !== void 0) {
|
|
1971
1981
|
constraints.push({
|
|
1972
1982
|
request: pythonVersionConfig.data,
|
|
1973
|
-
source:
|
|
1983
|
+
source: pythonVersionConfig.path,
|
|
1984
|
+
prettySource: pythonVersionConfig.path
|
|
1974
1985
|
});
|
|
1986
|
+
hasPythonVersionFile = true;
|
|
1975
1987
|
break;
|
|
1976
1988
|
}
|
|
1977
1989
|
}
|
|
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);
|
|
1990
|
+
if (!hasPythonVersionFile) {
|
|
1991
|
+
const manifestRequiresPython = manifest?.data.project?.["requires-python"];
|
|
1992
|
+
if (manifestRequiresPython) {
|
|
1993
|
+
const parsed = (0, import_specifier.parse)(manifestRequiresPython);
|
|
1992
1994
|
if (parsed?.length) {
|
|
1993
1995
|
const request = pythonRequestFromConstraint(parsed);
|
|
1994
1996
|
constraints.push({
|
|
1995
1997
|
request: [request],
|
|
1996
|
-
source:
|
|
1998
|
+
source: manifest.path,
|
|
1999
|
+
prettySource: `"requires-python" key in ${manifest.path}`
|
|
1997
2000
|
});
|
|
1998
2001
|
}
|
|
2002
|
+
} else {
|
|
2003
|
+
const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
|
|
2004
|
+
if (workspaceRequiresPython) {
|
|
2005
|
+
const parsed = (0, import_specifier.parse)(workspaceRequiresPython);
|
|
2006
|
+
if (parsed?.length) {
|
|
2007
|
+
const request = pythonRequestFromConstraint(parsed);
|
|
2008
|
+
constraints.push({
|
|
2009
|
+
request: [request],
|
|
2010
|
+
source: workspaceManifest.path,
|
|
2011
|
+
prettySource: `"requires-python" key in ${workspaceManifest.path}`
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1999
2015
|
}
|
|
2000
2016
|
}
|
|
2001
2017
|
return constraints;
|
|
@@ -2407,13 +2423,15 @@ function selectPython(constraints, available) {
|
|
|
2407
2423
|
}
|
|
2408
2424
|
}
|
|
2409
2425
|
if (constraintsWithMatches.length > 1) {
|
|
2410
|
-
const sources = constraintsWithMatches.map(
|
|
2426
|
+
const sources = constraintsWithMatches.map(
|
|
2427
|
+
(i) => constraints[i].prettySource
|
|
2428
|
+
);
|
|
2411
2429
|
warnings.push(
|
|
2412
2430
|
`Python version constraints may not overlap: ${sources.join(", ")}`
|
|
2413
2431
|
);
|
|
2414
2432
|
}
|
|
2415
2433
|
}
|
|
2416
|
-
const constraintDescriptions = constraints.map((c) => c.
|
|
2434
|
+
const constraintDescriptions = constraints.map((c) => c.prettySource).join(", ");
|
|
2417
2435
|
errors.push(
|
|
2418
2436
|
`No Python build satisfies all constraints: ${constraintDescriptions}`
|
|
2419
2437
|
);
|
|
@@ -2423,6 +2441,126 @@ function selectPython(constraints, available) {
|
|
|
2423
2441
|
warnings: warnings.length > 0 ? warnings : void 0
|
|
2424
2442
|
};
|
|
2425
2443
|
}
|
|
2444
|
+
function selectPythonVersion({
|
|
2445
|
+
constraints,
|
|
2446
|
+
availableBuilds,
|
|
2447
|
+
allBuilds,
|
|
2448
|
+
defaultBuild,
|
|
2449
|
+
majorMinorOnly,
|
|
2450
|
+
legacyTildeEquals
|
|
2451
|
+
}) {
|
|
2452
|
+
const source = constraints?.[0]?.source;
|
|
2453
|
+
if (!constraints || constraints.length === 0) {
|
|
2454
|
+
return { build: defaultBuild };
|
|
2455
|
+
}
|
|
2456
|
+
let effectiveConstraints = majorMinorOnly ? constraints.map((c) => ({
|
|
2457
|
+
...c,
|
|
2458
|
+
request: c.request.map(truncatePatchVersionsInRequest)
|
|
2459
|
+
})) : constraints;
|
|
2460
|
+
if (legacyTildeEquals) {
|
|
2461
|
+
effectiveConstraints = effectiveConstraints.map((c) => ({
|
|
2462
|
+
...c,
|
|
2463
|
+
request: c.request.map(legacyTildeEqualsTransform)
|
|
2464
|
+
}));
|
|
2465
|
+
}
|
|
2466
|
+
const result = selectPython(effectiveConstraints, availableBuilds);
|
|
2467
|
+
if (result.build) {
|
|
2468
|
+
return { build: result.build, source };
|
|
2469
|
+
}
|
|
2470
|
+
const allResult = selectPython(effectiveConstraints, allBuilds);
|
|
2471
|
+
if (allResult.build) {
|
|
2472
|
+
const version = pythonVersionToString(allResult.build.version);
|
|
2473
|
+
return {
|
|
2474
|
+
build: defaultBuild,
|
|
2475
|
+
source,
|
|
2476
|
+
notAvailable: { build: allResult.build, version }
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
const versionString = extractConstraintVersionString(constraints);
|
|
2480
|
+
return {
|
|
2481
|
+
build: defaultBuild,
|
|
2482
|
+
source,
|
|
2483
|
+
invalidConstraint: { versionString }
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
function extractConstraintVersionString(constraints) {
|
|
2487
|
+
for (const c of constraints) {
|
|
2488
|
+
for (const req of c.request) {
|
|
2489
|
+
if (req.version?.constraint && req.version.constraint.length > 0) {
|
|
2490
|
+
const specs = req.version.constraint;
|
|
2491
|
+
if (specs.length === 1 && specs[0].operator === "==" && (!specs[0].prefix || specs[0].prefix === ".*")) {
|
|
2492
|
+
return specs[0].version;
|
|
2493
|
+
}
|
|
2494
|
+
return pep440ConstraintsToString(specs);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
return "unknown";
|
|
2499
|
+
}
|
|
2500
|
+
function truncatePatchVersionsInRequest(req) {
|
|
2501
|
+
if (!req.version?.constraint || req.version.constraint.length === 0) {
|
|
2502
|
+
return req;
|
|
2503
|
+
}
|
|
2504
|
+
const newConstraints = [];
|
|
2505
|
+
for (const c of req.version.constraint) {
|
|
2506
|
+
const result = truncatePatchConstraint(c);
|
|
2507
|
+
if (result !== null) {
|
|
2508
|
+
newConstraints.push(result);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
const newVersion = {
|
|
2512
|
+
...req.version,
|
|
2513
|
+
constraint: newConstraints
|
|
2514
|
+
};
|
|
2515
|
+
return { ...req, version: newVersion };
|
|
2516
|
+
}
|
|
2517
|
+
function truncatePatchConstraint(c) {
|
|
2518
|
+
if (c.operator === "===") {
|
|
2519
|
+
return c;
|
|
2520
|
+
}
|
|
2521
|
+
const parts = c.version.split(".");
|
|
2522
|
+
if (parts.length < 3) {
|
|
2523
|
+
return c;
|
|
2524
|
+
}
|
|
2525
|
+
const majorMinor = parts.slice(0, 2).join(".");
|
|
2526
|
+
const patch = parseInt(parts[2], 10);
|
|
2527
|
+
switch (c.operator) {
|
|
2528
|
+
case "==":
|
|
2529
|
+
return { operator: "==", version: majorMinor, prefix: ".*" };
|
|
2530
|
+
case "!=":
|
|
2531
|
+
return null;
|
|
2532
|
+
case "~=":
|
|
2533
|
+
return { operator: "==", version: majorMinor, prefix: ".*" };
|
|
2534
|
+
case ">=":
|
|
2535
|
+
return { operator: ">=", version: majorMinor, prefix: "" };
|
|
2536
|
+
case "<=":
|
|
2537
|
+
return { operator: "<=", version: majorMinor, prefix: "" };
|
|
2538
|
+
case ">":
|
|
2539
|
+
return patch === 0 ? { operator: ">", version: majorMinor, prefix: "" } : { operator: ">=", version: majorMinor, prefix: "" };
|
|
2540
|
+
case "<":
|
|
2541
|
+
return patch === 0 ? { operator: "<", version: majorMinor, prefix: "" } : { operator: "<=", version: majorMinor, prefix: "" };
|
|
2542
|
+
default:
|
|
2543
|
+
return c;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
function legacyTildeEqualsTransform(req) {
|
|
2547
|
+
if (!req.version?.constraint || req.version.constraint.length === 0) {
|
|
2548
|
+
return req;
|
|
2549
|
+
}
|
|
2550
|
+
const newConstraints = req.version.constraint.map((c) => {
|
|
2551
|
+
if (c.operator !== "~=")
|
|
2552
|
+
return c;
|
|
2553
|
+
const parts = c.version.split(".");
|
|
2554
|
+
if (parts.length === 2) {
|
|
2555
|
+
return { operator: "==", version: c.version, prefix: ".*" };
|
|
2556
|
+
}
|
|
2557
|
+
return c;
|
|
2558
|
+
});
|
|
2559
|
+
return {
|
|
2560
|
+
...req,
|
|
2561
|
+
version: { ...req.version, constraint: newConstraints }
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2426
2564
|
function pythonVersionToString(version) {
|
|
2427
2565
|
let str = `${version.major}.${version.minor}`;
|
|
2428
2566
|
if (version.patch !== void 0) {
|
|
@@ -2434,7 +2572,7 @@ function pythonVersionToString(version) {
|
|
|
2434
2572
|
return str;
|
|
2435
2573
|
}
|
|
2436
2574
|
function pep440ConstraintsToString(constraints) {
|
|
2437
|
-
return constraints.map((c) => `${c.operator}${c.
|
|
2575
|
+
return constraints.map((c) => `${c.operator}${c.version}${c.prefix ?? ""}`).join(",");
|
|
2438
2576
|
}
|
|
2439
2577
|
function implementationsMatch(buildImpl, requestImpl) {
|
|
2440
2578
|
if (PythonImplementation.isUnknown(buildImpl)) {
|
|
@@ -2552,15 +2690,16 @@ var HashDigestSchema = hashDigestSchema;
|
|
|
2552
2690
|
UvIndexEntrySchema,
|
|
2553
2691
|
classifyPackages,
|
|
2554
2692
|
containsAppOrHandler,
|
|
2693
|
+
containsTopLevelCallable,
|
|
2555
2694
|
createMinimalManifest,
|
|
2556
2695
|
discoverPythonPackage,
|
|
2557
2696
|
extendDistRecord,
|
|
2558
2697
|
getStringConstant,
|
|
2559
2698
|
isPrivatePackageSource,
|
|
2560
2699
|
normalizePackageName,
|
|
2561
|
-
parseDjangoSettingsModule,
|
|
2562
2700
|
parseUvLock,
|
|
2563
2701
|
scanDistributions,
|
|
2564
2702
|
selectPython,
|
|
2703
|
+
selectPythonVersion,
|
|
2565
2704
|
stringifyManifest
|
|
2566
2705
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @vercel/python-analysis
|
|
8
8
|
*/
|
|
9
|
-
export { containsAppOrHandler,
|
|
9
|
+
export { containsAppOrHandler, containsTopLevelCallable, getStringConstant, } from './semantic/entrypoints';
|
|
10
10
|
export type { Distribution, DistributionIndex, PackagePath, DirectUrlInfo, } from './manifest/dist-metadata';
|
|
11
11
|
export { extendDistRecord, scanDistributions } from './manifest/dist-metadata';
|
|
12
12
|
export type { PythonConfig, PythonConfigs, PythonLockFile, PythonManifest, PythonManifestOrigin, PythonPackage, PythonVersionConfig, } from './manifest/package';
|
|
@@ -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
|
@@ -46,17 +46,16 @@ async function containsAppOrHandler(source) {
|
|
|
46
46
|
const mod = await importWasmModule();
|
|
47
47
|
return mod.containsAppOrHandler(source);
|
|
48
48
|
}
|
|
49
|
-
async function
|
|
49
|
+
async function containsTopLevelCallable(source, name) {
|
|
50
|
+
if (!source.includes(name)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
50
53
|
const mod = await importWasmModule();
|
|
51
|
-
return mod.
|
|
54
|
+
return mod.containsTopLevelCallable(source, name);
|
|
52
55
|
}
|
|
53
|
-
|
|
54
|
-
async function parseDjangoSettingsModule(content) {
|
|
55
|
-
if (!DJANGO_SETTINGS_MODULE_PATTERN_RE.test(content)) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
56
|
+
async function getStringConstant(source, name) {
|
|
58
57
|
const mod = await importWasmModule();
|
|
59
|
-
return mod.
|
|
58
|
+
return mod.getStringConstant(source, name) ?? null;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
// src/manifest/dist-metadata.ts
|
|
@@ -662,17 +661,24 @@ function convertPipfileToPyprojectToml(pipfile) {
|
|
|
662
661
|
function convertPipfileLockToPyprojectToml(pipfileLock) {
|
|
663
662
|
const sources = {};
|
|
664
663
|
const pyproject = {};
|
|
664
|
+
const pythonVersion = pipfileLock._meta?.requires?.python_version;
|
|
665
665
|
const deps = [];
|
|
666
666
|
for (const dep of pipfileLockDepsToRequirements(pipfileLock.default || {})) {
|
|
667
667
|
deps.push(formatPep508(dep));
|
|
668
668
|
addDepSource(sources, dep);
|
|
669
669
|
}
|
|
670
|
-
if (deps.length > 0) {
|
|
671
|
-
|
|
670
|
+
if (deps.length > 0 || pythonVersion) {
|
|
671
|
+
const project = {
|
|
672
672
|
name: "app",
|
|
673
|
-
version: "0.1.0"
|
|
674
|
-
dependencies: deps
|
|
673
|
+
version: "0.1.0"
|
|
675
674
|
};
|
|
675
|
+
if (pythonVersion) {
|
|
676
|
+
project["requires-python"] = `==${pythonVersion}.*`;
|
|
677
|
+
}
|
|
678
|
+
if (deps.length > 0) {
|
|
679
|
+
project.dependencies = deps;
|
|
680
|
+
}
|
|
681
|
+
pyproject.project = project;
|
|
676
682
|
}
|
|
677
683
|
const devDeps = [];
|
|
678
684
|
for (const dep of pipfileLockDepsToRequirements(pipfileLock.develop || {})) {
|
|
@@ -1853,8 +1859,10 @@ async function discoverPythonPackage({
|
|
|
1853
1859
|
let workspaceManifest;
|
|
1854
1860
|
let workspaceLockFile;
|
|
1855
1861
|
if (manifests.length === 0) {
|
|
1862
|
+
const requiresPython2 = computeRequiresPython(void 0, void 0, configs);
|
|
1856
1863
|
return {
|
|
1857
|
-
configs
|
|
1864
|
+
configs,
|
|
1865
|
+
requiresPython: requiresPython2
|
|
1858
1866
|
};
|
|
1859
1867
|
} else {
|
|
1860
1868
|
entrypointManifest = manifests[0];
|
|
@@ -1888,37 +1896,44 @@ async function discoverPythonPackage({
|
|
|
1888
1896
|
}
|
|
1889
1897
|
function computeRequiresPython(manifest, workspaceManifest, configs) {
|
|
1890
1898
|
const constraints = [];
|
|
1899
|
+
let hasPythonVersionFile = false;
|
|
1891
1900
|
for (const configSet of configs) {
|
|
1892
1901
|
const pythonVersionConfig = configSet[".python-version" /* PythonVersion */];
|
|
1893
1902
|
if (pythonVersionConfig !== void 0) {
|
|
1894
1903
|
constraints.push({
|
|
1895
1904
|
request: pythonVersionConfig.data,
|
|
1896
|
-
source:
|
|
1905
|
+
source: pythonVersionConfig.path,
|
|
1906
|
+
prettySource: pythonVersionConfig.path
|
|
1897
1907
|
});
|
|
1908
|
+
hasPythonVersionFile = true;
|
|
1898
1909
|
break;
|
|
1899
1910
|
}
|
|
1900
1911
|
}
|
|
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);
|
|
1912
|
+
if (!hasPythonVersionFile) {
|
|
1913
|
+
const manifestRequiresPython = manifest?.data.project?.["requires-python"];
|
|
1914
|
+
if (manifestRequiresPython) {
|
|
1915
|
+
const parsed = parse2(manifestRequiresPython);
|
|
1915
1916
|
if (parsed?.length) {
|
|
1916
1917
|
const request = pythonRequestFromConstraint(parsed);
|
|
1917
1918
|
constraints.push({
|
|
1918
1919
|
request: [request],
|
|
1919
|
-
source:
|
|
1920
|
+
source: manifest.path,
|
|
1921
|
+
prettySource: `"requires-python" key in ${manifest.path}`
|
|
1920
1922
|
});
|
|
1921
1923
|
}
|
|
1924
|
+
} else {
|
|
1925
|
+
const workspaceRequiresPython = workspaceManifest?.data.project?.["requires-python"];
|
|
1926
|
+
if (workspaceRequiresPython) {
|
|
1927
|
+
const parsed = parse2(workspaceRequiresPython);
|
|
1928
|
+
if (parsed?.length) {
|
|
1929
|
+
const request = pythonRequestFromConstraint(parsed);
|
|
1930
|
+
constraints.push({
|
|
1931
|
+
request: [request],
|
|
1932
|
+
source: workspaceManifest.path,
|
|
1933
|
+
prettySource: `"requires-python" key in ${workspaceManifest.path}`
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1922
1937
|
}
|
|
1923
1938
|
}
|
|
1924
1939
|
return constraints;
|
|
@@ -2330,13 +2345,15 @@ function selectPython(constraints, available) {
|
|
|
2330
2345
|
}
|
|
2331
2346
|
}
|
|
2332
2347
|
if (constraintsWithMatches.length > 1) {
|
|
2333
|
-
const sources = constraintsWithMatches.map(
|
|
2348
|
+
const sources = constraintsWithMatches.map(
|
|
2349
|
+
(i) => constraints[i].prettySource
|
|
2350
|
+
);
|
|
2334
2351
|
warnings.push(
|
|
2335
2352
|
`Python version constraints may not overlap: ${sources.join(", ")}`
|
|
2336
2353
|
);
|
|
2337
2354
|
}
|
|
2338
2355
|
}
|
|
2339
|
-
const constraintDescriptions = constraints.map((c) => c.
|
|
2356
|
+
const constraintDescriptions = constraints.map((c) => c.prettySource).join(", ");
|
|
2340
2357
|
errors.push(
|
|
2341
2358
|
`No Python build satisfies all constraints: ${constraintDescriptions}`
|
|
2342
2359
|
);
|
|
@@ -2346,6 +2363,126 @@ function selectPython(constraints, available) {
|
|
|
2346
2363
|
warnings: warnings.length > 0 ? warnings : void 0
|
|
2347
2364
|
};
|
|
2348
2365
|
}
|
|
2366
|
+
function selectPythonVersion({
|
|
2367
|
+
constraints,
|
|
2368
|
+
availableBuilds,
|
|
2369
|
+
allBuilds,
|
|
2370
|
+
defaultBuild,
|
|
2371
|
+
majorMinorOnly,
|
|
2372
|
+
legacyTildeEquals
|
|
2373
|
+
}) {
|
|
2374
|
+
const source = constraints?.[0]?.source;
|
|
2375
|
+
if (!constraints || constraints.length === 0) {
|
|
2376
|
+
return { build: defaultBuild };
|
|
2377
|
+
}
|
|
2378
|
+
let effectiveConstraints = majorMinorOnly ? constraints.map((c) => ({
|
|
2379
|
+
...c,
|
|
2380
|
+
request: c.request.map(truncatePatchVersionsInRequest)
|
|
2381
|
+
})) : constraints;
|
|
2382
|
+
if (legacyTildeEquals) {
|
|
2383
|
+
effectiveConstraints = effectiveConstraints.map((c) => ({
|
|
2384
|
+
...c,
|
|
2385
|
+
request: c.request.map(legacyTildeEqualsTransform)
|
|
2386
|
+
}));
|
|
2387
|
+
}
|
|
2388
|
+
const result = selectPython(effectiveConstraints, availableBuilds);
|
|
2389
|
+
if (result.build) {
|
|
2390
|
+
return { build: result.build, source };
|
|
2391
|
+
}
|
|
2392
|
+
const allResult = selectPython(effectiveConstraints, allBuilds);
|
|
2393
|
+
if (allResult.build) {
|
|
2394
|
+
const version = pythonVersionToString(allResult.build.version);
|
|
2395
|
+
return {
|
|
2396
|
+
build: defaultBuild,
|
|
2397
|
+
source,
|
|
2398
|
+
notAvailable: { build: allResult.build, version }
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
const versionString = extractConstraintVersionString(constraints);
|
|
2402
|
+
return {
|
|
2403
|
+
build: defaultBuild,
|
|
2404
|
+
source,
|
|
2405
|
+
invalidConstraint: { versionString }
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
function extractConstraintVersionString(constraints) {
|
|
2409
|
+
for (const c of constraints) {
|
|
2410
|
+
for (const req of c.request) {
|
|
2411
|
+
if (req.version?.constraint && req.version.constraint.length > 0) {
|
|
2412
|
+
const specs = req.version.constraint;
|
|
2413
|
+
if (specs.length === 1 && specs[0].operator === "==" && (!specs[0].prefix || specs[0].prefix === ".*")) {
|
|
2414
|
+
return specs[0].version;
|
|
2415
|
+
}
|
|
2416
|
+
return pep440ConstraintsToString(specs);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
return "unknown";
|
|
2421
|
+
}
|
|
2422
|
+
function truncatePatchVersionsInRequest(req) {
|
|
2423
|
+
if (!req.version?.constraint || req.version.constraint.length === 0) {
|
|
2424
|
+
return req;
|
|
2425
|
+
}
|
|
2426
|
+
const newConstraints = [];
|
|
2427
|
+
for (const c of req.version.constraint) {
|
|
2428
|
+
const result = truncatePatchConstraint(c);
|
|
2429
|
+
if (result !== null) {
|
|
2430
|
+
newConstraints.push(result);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
const newVersion = {
|
|
2434
|
+
...req.version,
|
|
2435
|
+
constraint: newConstraints
|
|
2436
|
+
};
|
|
2437
|
+
return { ...req, version: newVersion };
|
|
2438
|
+
}
|
|
2439
|
+
function truncatePatchConstraint(c) {
|
|
2440
|
+
if (c.operator === "===") {
|
|
2441
|
+
return c;
|
|
2442
|
+
}
|
|
2443
|
+
const parts = c.version.split(".");
|
|
2444
|
+
if (parts.length < 3) {
|
|
2445
|
+
return c;
|
|
2446
|
+
}
|
|
2447
|
+
const majorMinor = parts.slice(0, 2).join(".");
|
|
2448
|
+
const patch = parseInt(parts[2], 10);
|
|
2449
|
+
switch (c.operator) {
|
|
2450
|
+
case "==":
|
|
2451
|
+
return { operator: "==", version: majorMinor, prefix: ".*" };
|
|
2452
|
+
case "!=":
|
|
2453
|
+
return null;
|
|
2454
|
+
case "~=":
|
|
2455
|
+
return { operator: "==", version: majorMinor, prefix: ".*" };
|
|
2456
|
+
case ">=":
|
|
2457
|
+
return { operator: ">=", version: majorMinor, prefix: "" };
|
|
2458
|
+
case "<=":
|
|
2459
|
+
return { operator: "<=", version: majorMinor, prefix: "" };
|
|
2460
|
+
case ">":
|
|
2461
|
+
return patch === 0 ? { operator: ">", version: majorMinor, prefix: "" } : { operator: ">=", version: majorMinor, prefix: "" };
|
|
2462
|
+
case "<":
|
|
2463
|
+
return patch === 0 ? { operator: "<", version: majorMinor, prefix: "" } : { operator: "<=", version: majorMinor, prefix: "" };
|
|
2464
|
+
default:
|
|
2465
|
+
return c;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function legacyTildeEqualsTransform(req) {
|
|
2469
|
+
if (!req.version?.constraint || req.version.constraint.length === 0) {
|
|
2470
|
+
return req;
|
|
2471
|
+
}
|
|
2472
|
+
const newConstraints = req.version.constraint.map((c) => {
|
|
2473
|
+
if (c.operator !== "~=")
|
|
2474
|
+
return c;
|
|
2475
|
+
const parts = c.version.split(".");
|
|
2476
|
+
if (parts.length === 2) {
|
|
2477
|
+
return { operator: "==", version: c.version, prefix: ".*" };
|
|
2478
|
+
}
|
|
2479
|
+
return c;
|
|
2480
|
+
});
|
|
2481
|
+
return {
|
|
2482
|
+
...req,
|
|
2483
|
+
version: { ...req.version, constraint: newConstraints }
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2349
2486
|
function pythonVersionToString(version) {
|
|
2350
2487
|
let str = `${version.major}.${version.minor}`;
|
|
2351
2488
|
if (version.patch !== void 0) {
|
|
@@ -2357,7 +2494,7 @@ function pythonVersionToString(version) {
|
|
|
2357
2494
|
return str;
|
|
2358
2495
|
}
|
|
2359
2496
|
function pep440ConstraintsToString(constraints) {
|
|
2360
|
-
return constraints.map((c) => `${c.operator}${c.
|
|
2497
|
+
return constraints.map((c) => `${c.operator}${c.version}${c.prefix ?? ""}`).join(",");
|
|
2361
2498
|
}
|
|
2362
2499
|
function implementationsMatch(buildImpl, requestImpl) {
|
|
2363
2500
|
if (PythonImplementation.isUnknown(buildImpl)) {
|
|
@@ -2474,15 +2611,16 @@ export {
|
|
|
2474
2611
|
UvIndexEntrySchema,
|
|
2475
2612
|
classifyPackages,
|
|
2476
2613
|
containsAppOrHandler,
|
|
2614
|
+
containsTopLevelCallable,
|
|
2477
2615
|
createMinimalManifest,
|
|
2478
2616
|
discoverPythonPackage,
|
|
2479
2617
|
extendDistRecord,
|
|
2480
2618
|
getStringConstant,
|
|
2481
2619
|
isPrivatePackageSource,
|
|
2482
2620
|
normalizePackageName2 as normalizePackageName,
|
|
2483
|
-
parseDjangoSettingsModule,
|
|
2484
2621
|
parseUvLock,
|
|
2485
2622
|
scanDistributions,
|
|
2486
2623
|
selectPython,
|
|
2624
|
+
selectPythonVersion,
|
|
2487
2625
|
stringifyManifest
|
|
2488
2626
|
};
|
|
@@ -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;
|