@vercel/python-analysis 0.3.2 → 0.4.1
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 +332 -19
- package/dist/index.d.ts +3 -2
- package/dist/index.js +328 -18
- package/dist/manifest/package.d.ts +24 -0
- package/dist/manifest/pyproject/schema.zod.d.ts +16 -0
- package/dist/manifest/requirements-txt-parser.d.ts +4 -0
- package/dist/manifest/serialize.d.ts +22 -0
- package/dist/manifest/uv-config/schema.zod.d.ts +8 -0
- package/dist/manifest/uv-config/types.d.ts +2 -0
- package/dist/util/error.d.ts +7 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -66,7 +66,14 @@ var isErrnoException = (error, code = void 0) => {
|
|
|
66
66
|
return util.types.isNativeError(error) && "code" in error && (code === void 0 || error.code === code);
|
|
67
67
|
};
|
|
68
68
|
var PythonAnalysisError = class extends Error {
|
|
69
|
-
constructor({
|
|
69
|
+
constructor({
|
|
70
|
+
message,
|
|
71
|
+
code,
|
|
72
|
+
path: path4,
|
|
73
|
+
link,
|
|
74
|
+
action,
|
|
75
|
+
fileContent
|
|
76
|
+
}) {
|
|
70
77
|
super(message);
|
|
71
78
|
this.hideStackTrace = true;
|
|
72
79
|
this.name = "PythonAnalysisError";
|
|
@@ -74,6 +81,7 @@ var PythonAnalysisError = class extends Error {
|
|
|
74
81
|
this.path = path4;
|
|
75
82
|
this.link = link;
|
|
76
83
|
this.action = action;
|
|
84
|
+
this.fileContent = fileContent;
|
|
77
85
|
}
|
|
78
86
|
};
|
|
79
87
|
|
|
@@ -135,7 +143,8 @@ function parseRawConfig(content, filename, filetype = void 0) {
|
|
|
135
143
|
throw new PythonAnalysisError({
|
|
136
144
|
message: `Could not parse config file "${filename}": ${error.message}`,
|
|
137
145
|
code: "PYTHON_CONFIG_PARSE_ERROR",
|
|
138
|
-
path: filename
|
|
146
|
+
path: filename,
|
|
147
|
+
fileContent: content
|
|
139
148
|
});
|
|
140
149
|
}
|
|
141
150
|
throw error;
|
|
@@ -153,7 +162,8 @@ function parseConfig(content, filename, schema, filetype = void 0) {
|
|
|
153
162
|
message: `Invalid config in "${filename}":
|
|
154
163
|
${issues}`,
|
|
155
164
|
code: "PYTHON_CONFIG_VALIDATION_ERROR",
|
|
156
|
-
path: filename
|
|
165
|
+
path: filename,
|
|
166
|
+
fileContent: content
|
|
157
167
|
});
|
|
158
168
|
}
|
|
159
169
|
return result.data;
|
|
@@ -435,6 +445,8 @@ function convertPipfileToPyprojectToml(pipfile) {
|
|
|
435
445
|
}
|
|
436
446
|
if (deps.length > 0) {
|
|
437
447
|
pyproject.project = {
|
|
448
|
+
name: "app",
|
|
449
|
+
version: "0.1.0",
|
|
438
450
|
dependencies: deps
|
|
439
451
|
};
|
|
440
452
|
}
|
|
@@ -492,6 +504,8 @@ function convertPipfileLockToPyprojectToml(pipfileLock) {
|
|
|
492
504
|
}
|
|
493
505
|
if (deps.length > 0) {
|
|
494
506
|
pyproject.project = {
|
|
507
|
+
name: "app",
|
|
508
|
+
version: "0.1.0",
|
|
495
509
|
dependencies: deps
|
|
496
510
|
};
|
|
497
511
|
}
|
|
@@ -568,7 +582,8 @@ var uvIndexEntrySchema = z3.object({
|
|
|
568
582
|
name: z3.string(),
|
|
569
583
|
url: z3.string(),
|
|
570
584
|
default: z3.boolean().optional(),
|
|
571
|
-
explicit: z3.boolean().optional()
|
|
585
|
+
explicit: z3.boolean().optional(),
|
|
586
|
+
format: z3.string().optional()
|
|
572
587
|
});
|
|
573
588
|
var uvConfigSchema = z3.object({
|
|
574
589
|
sources: z3.record(z3.union([dependencySourceSchema, z3.array(dependencySourceSchema)])).optional(),
|
|
@@ -642,6 +657,7 @@ import { normalize } from "path";
|
|
|
642
657
|
import { parsePipRequirementsFile } from "pip-requirements-js";
|
|
643
658
|
var PRIMARY_INDEX_NAME = "primary";
|
|
644
659
|
var EXTRA_INDEX_PREFIX = "extra-";
|
|
660
|
+
var FIND_LINKS_PREFIX = "find-links-";
|
|
645
661
|
function parseGitUrl(url) {
|
|
646
662
|
if (!url.startsWith("git+")) {
|
|
647
663
|
return null;
|
|
@@ -683,6 +699,8 @@ function extractPipArguments(fileContent) {
|
|
|
683
699
|
};
|
|
684
700
|
const lines = fileContent.split(/\r?\n/);
|
|
685
701
|
const cleanedLines = [];
|
|
702
|
+
const pathRequirements = [];
|
|
703
|
+
const editableRequirements = [];
|
|
686
704
|
for (let i = 0; i < lines.length; i++) {
|
|
687
705
|
const line = lines[i];
|
|
688
706
|
const trimmed = line.trim();
|
|
@@ -697,11 +715,21 @@ function extractPipArguments(fileContent) {
|
|
|
697
715
|
fullLine = fullLine.slice(0, -1) + lines[i + linesConsumed].trim();
|
|
698
716
|
}
|
|
699
717
|
const extracted = tryExtractPipArgument(fullLine, options);
|
|
700
|
-
if (extracted) {
|
|
718
|
+
if (extracted === true) {
|
|
719
|
+
i += linesConsumed;
|
|
720
|
+
} else if (typeof extracted === "object" && extracted.editable) {
|
|
721
|
+
editableRequirements.push(extracted.editable);
|
|
701
722
|
i += linesConsumed;
|
|
702
723
|
} else {
|
|
724
|
+
if (/^-[a-zA-Z]/.test(fullLine) && !fullLine.startsWith("-r") && !fullLine.startsWith("-c")) {
|
|
725
|
+
i += linesConsumed;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
703
728
|
const strippedLine = stripInlineHashes(fullLine);
|
|
704
|
-
|
|
729
|
+
const effectiveLine = (strippedLine !== fullLine ? strippedLine : fullLine).trim();
|
|
730
|
+
if (isPathOrUrlRequirement(effectiveLine)) {
|
|
731
|
+
pathRequirements.push(effectiveLine);
|
|
732
|
+
} else if (strippedLine !== fullLine) {
|
|
705
733
|
cleanedLines.push(strippedLine);
|
|
706
734
|
} else {
|
|
707
735
|
cleanedLines.push(line);
|
|
@@ -714,7 +742,9 @@ function extractPipArguments(fileContent) {
|
|
|
714
742
|
}
|
|
715
743
|
return {
|
|
716
744
|
cleanedContent: cleanedLines.join("\n"),
|
|
717
|
-
options
|
|
745
|
+
options,
|
|
746
|
+
pathRequirements,
|
|
747
|
+
editableRequirements
|
|
718
748
|
};
|
|
719
749
|
}
|
|
720
750
|
function tryExtractPipArgument(line, options) {
|
|
@@ -753,6 +783,46 @@ function tryExtractPipArgument(line, options) {
|
|
|
753
783
|
return true;
|
|
754
784
|
}
|
|
755
785
|
}
|
|
786
|
+
if (line.startsWith("--editable")) {
|
|
787
|
+
const path4 = extractArgValue(line, "--editable");
|
|
788
|
+
if (path4) {
|
|
789
|
+
return { editable: path4 };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (line.startsWith("-e ") || line.startsWith("-e ")) {
|
|
793
|
+
const path4 = line.slice(2).trim();
|
|
794
|
+
if (path4) {
|
|
795
|
+
return { editable: path4 };
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (line.startsWith("--find-links")) {
|
|
799
|
+
const url = extractArgValue(line, "--find-links");
|
|
800
|
+
if (url) {
|
|
801
|
+
if (!options.findLinks)
|
|
802
|
+
options.findLinks = [];
|
|
803
|
+
options.findLinks.push(url);
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (line.startsWith("-f ") || line.startsWith("-f ")) {
|
|
808
|
+
const match = line.match(/^-f\s+(\S+)/);
|
|
809
|
+
if (match) {
|
|
810
|
+
if (!options.findLinks)
|
|
811
|
+
options.findLinks = [];
|
|
812
|
+
options.findLinks.push(match[1]);
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (line === "--no-index" || line.startsWith("--no-index ")) {
|
|
817
|
+
options.noIndex = true;
|
|
818
|
+
return true;
|
|
819
|
+
}
|
|
820
|
+
if (line.startsWith("--no-binary") || line.startsWith("--only-binary")) {
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
if (line.startsWith("--")) {
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
756
826
|
return false;
|
|
757
827
|
}
|
|
758
828
|
function stripInlineHashes(line) {
|
|
@@ -768,15 +838,158 @@ function extractInlineHashes(line) {
|
|
|
768
838
|
return hashes;
|
|
769
839
|
}
|
|
770
840
|
function extractArgValue(line, option) {
|
|
841
|
+
let value = null;
|
|
771
842
|
if (line.startsWith(`${option}=`)) {
|
|
772
|
-
|
|
773
|
-
|
|
843
|
+
value = line.slice(option.length + 1).trim();
|
|
844
|
+
} else if (line.startsWith(`${option} `) || line.startsWith(`${option} `)) {
|
|
845
|
+
value = line.slice(option.length).trim();
|
|
774
846
|
}
|
|
775
|
-
if (
|
|
776
|
-
|
|
777
|
-
|
|
847
|
+
if (!value)
|
|
848
|
+
return null;
|
|
849
|
+
const commentIdx = value.indexOf(" #");
|
|
850
|
+
if (commentIdx !== -1) {
|
|
851
|
+
value = value.slice(0, commentIdx).trim();
|
|
778
852
|
}
|
|
779
|
-
return null;
|
|
853
|
+
return value || null;
|
|
854
|
+
}
|
|
855
|
+
function isPathOrUrlRequirement(line) {
|
|
856
|
+
if (line.startsWith("./") || line.startsWith("../"))
|
|
857
|
+
return true;
|
|
858
|
+
if (line.startsWith("/"))
|
|
859
|
+
return true;
|
|
860
|
+
if (line.startsWith("~/"))
|
|
861
|
+
return true;
|
|
862
|
+
if (/^(https?|ftp|file):\/\//i.test(line))
|
|
863
|
+
return true;
|
|
864
|
+
if (isBareArchiveFilename(line))
|
|
865
|
+
return true;
|
|
866
|
+
return false;
|
|
867
|
+
}
|
|
868
|
+
function isBareArchiveFilename(line) {
|
|
869
|
+
if (line.includes(" @ "))
|
|
870
|
+
return false;
|
|
871
|
+
let check = line;
|
|
872
|
+
const commentIdx = check.indexOf(" #");
|
|
873
|
+
if (commentIdx !== -1)
|
|
874
|
+
check = check.slice(0, commentIdx);
|
|
875
|
+
const markerIdx = check.indexOf(" ;");
|
|
876
|
+
if (markerIdx !== -1)
|
|
877
|
+
check = check.slice(0, markerIdx);
|
|
878
|
+
const extrasIdx = check.indexOf("[");
|
|
879
|
+
if (extrasIdx !== -1)
|
|
880
|
+
check = check.slice(0, extrasIdx);
|
|
881
|
+
check = check.trim();
|
|
882
|
+
return /\.(whl|tar\.gz|tar\.bz2|tar\.xz|zip)$/i.test(check);
|
|
883
|
+
}
|
|
884
|
+
function parseWheelFilename(filename) {
|
|
885
|
+
if (!filename.endsWith(".whl"))
|
|
886
|
+
return null;
|
|
887
|
+
const stem = filename.slice(0, -4);
|
|
888
|
+
const parts = stem.split("-");
|
|
889
|
+
if (parts.length < 5 || parts.length > 6)
|
|
890
|
+
return null;
|
|
891
|
+
const name = parts[0];
|
|
892
|
+
const version = parts[1];
|
|
893
|
+
if (!name || !version)
|
|
894
|
+
return null;
|
|
895
|
+
return {
|
|
896
|
+
name: name.replace(/_/g, "-"),
|
|
897
|
+
version
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function parseSdistFilename(filename) {
|
|
901
|
+
let stem = filename;
|
|
902
|
+
for (const ext of [".tar.gz", ".tar.bz2", ".tar.xz", ".zip"]) {
|
|
903
|
+
if (stem.endsWith(ext)) {
|
|
904
|
+
stem = stem.slice(0, -ext.length);
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (stem === filename)
|
|
909
|
+
return null;
|
|
910
|
+
const parts = stem.split("-");
|
|
911
|
+
let versionIdx = -1;
|
|
912
|
+
for (let i = 1; i < parts.length; i++) {
|
|
913
|
+
if (/^\d/.test(parts[i])) {
|
|
914
|
+
versionIdx = i;
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (versionIdx === -1)
|
|
919
|
+
return null;
|
|
920
|
+
return {
|
|
921
|
+
name: parts.slice(0, versionIdx).join("-"),
|
|
922
|
+
version: parts.slice(versionIdx).join("-")
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function normalizePathRequirement(rawLine) {
|
|
926
|
+
let line = rawLine;
|
|
927
|
+
const commentIdx = line.indexOf(" #");
|
|
928
|
+
if (commentIdx !== -1) {
|
|
929
|
+
line = line.slice(0, commentIdx).trim();
|
|
930
|
+
}
|
|
931
|
+
let markers;
|
|
932
|
+
const markerIdx = line.indexOf(" ;");
|
|
933
|
+
if (markerIdx !== -1) {
|
|
934
|
+
markers = line.slice(markerIdx + 2).trim();
|
|
935
|
+
line = line.slice(0, markerIdx).trim();
|
|
936
|
+
}
|
|
937
|
+
let extras;
|
|
938
|
+
const extrasMatch = line.match(/\[([^\]]+)\]$/);
|
|
939
|
+
if (extrasMatch) {
|
|
940
|
+
extras = extrasMatch[1].split(",").map((e) => e.trim());
|
|
941
|
+
line = line.slice(0, extrasMatch.index).trim();
|
|
942
|
+
}
|
|
943
|
+
const isUrl = /^(https?|ftp|file):\/\//i.test(line);
|
|
944
|
+
let filename;
|
|
945
|
+
if (isUrl) {
|
|
946
|
+
try {
|
|
947
|
+
const url = new URL(line);
|
|
948
|
+
filename = url.pathname.split("/").pop() || "";
|
|
949
|
+
} catch {
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
} else {
|
|
953
|
+
const cleanPath = line.replace(/\/+$/, "");
|
|
954
|
+
filename = cleanPath.split("/").pop() || "";
|
|
955
|
+
}
|
|
956
|
+
if (!filename)
|
|
957
|
+
return null;
|
|
958
|
+
let name;
|
|
959
|
+
let version;
|
|
960
|
+
const wheelParsed = parseWheelFilename(filename);
|
|
961
|
+
if (wheelParsed) {
|
|
962
|
+
name = wheelParsed.name;
|
|
963
|
+
version = wheelParsed.version;
|
|
964
|
+
}
|
|
965
|
+
if (!name) {
|
|
966
|
+
const sdistParsed = parseSdistFilename(filename);
|
|
967
|
+
if (sdistParsed) {
|
|
968
|
+
name = sdistParsed.name;
|
|
969
|
+
version = sdistParsed.version;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
if (!name) {
|
|
973
|
+
name = filename.replace(/[-_.]+/g, "-").toLowerCase();
|
|
974
|
+
}
|
|
975
|
+
if (!name)
|
|
976
|
+
return null;
|
|
977
|
+
const req = { name };
|
|
978
|
+
if (version) {
|
|
979
|
+
req.version = `==${version}`;
|
|
980
|
+
}
|
|
981
|
+
if (extras && extras.length > 0) {
|
|
982
|
+
req.extras = extras;
|
|
983
|
+
}
|
|
984
|
+
if (markers) {
|
|
985
|
+
req.markers = markers;
|
|
986
|
+
}
|
|
987
|
+
if (isUrl) {
|
|
988
|
+
req.url = line;
|
|
989
|
+
} else {
|
|
990
|
+
req.source = { path: line };
|
|
991
|
+
}
|
|
992
|
+
return req;
|
|
780
993
|
}
|
|
781
994
|
function convertRequirementsToPyprojectToml(fileContent, readFile3) {
|
|
782
995
|
const pyproject = {};
|
|
@@ -795,6 +1008,8 @@ function convertRequirementsToPyprojectToml(fileContent, readFile3) {
|
|
|
795
1008
|
}
|
|
796
1009
|
if (deps.length > 0) {
|
|
797
1010
|
pyproject.project = {
|
|
1011
|
+
name: "app",
|
|
1012
|
+
version: "0.1.0",
|
|
798
1013
|
dependencies: deps
|
|
799
1014
|
};
|
|
800
1015
|
}
|
|
@@ -826,6 +1041,15 @@ function buildIndexEntries(pipOptions) {
|
|
|
826
1041
|
url: pipOptions.extraIndexUrls[i]
|
|
827
1042
|
});
|
|
828
1043
|
}
|
|
1044
|
+
if (pipOptions.findLinks) {
|
|
1045
|
+
for (let i = 0; i < pipOptions.findLinks.length; i++) {
|
|
1046
|
+
indexes.push({
|
|
1047
|
+
name: `${FIND_LINKS_PREFIX}${i + 1}`,
|
|
1048
|
+
url: pipOptions.findLinks[i],
|
|
1049
|
+
format: "flat"
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
829
1053
|
return indexes;
|
|
830
1054
|
}
|
|
831
1055
|
function parseRequirementsFile(fileContent, readFile3) {
|
|
@@ -833,7 +1057,7 @@ function parseRequirementsFile(fileContent, readFile3) {
|
|
|
833
1057
|
return parseRequirementsFileInternal(fileContent, readFile3, visited);
|
|
834
1058
|
}
|
|
835
1059
|
function parseRequirementsFileInternal(fileContent, readFile3, visited) {
|
|
836
|
-
const { cleanedContent, options } = extractPipArguments(fileContent);
|
|
1060
|
+
const { cleanedContent, options, pathRequirements, editableRequirements } = extractPipArguments(fileContent);
|
|
837
1061
|
const hashMap = buildHashMap(fileContent);
|
|
838
1062
|
const requirements = parsePipRequirementsFile(cleanedContent);
|
|
839
1063
|
const normalized = [];
|
|
@@ -841,7 +1065,9 @@ function parseRequirementsFileInternal(fileContent, readFile3, visited) {
|
|
|
841
1065
|
requirementFiles: [...options.requirementFiles],
|
|
842
1066
|
constraintFiles: [...options.constraintFiles],
|
|
843
1067
|
indexUrl: options.indexUrl,
|
|
844
|
-
extraIndexUrls: [...options.extraIndexUrls]
|
|
1068
|
+
extraIndexUrls: [...options.extraIndexUrls],
|
|
1069
|
+
findLinks: options.findLinks ? [...options.findLinks] : void 0,
|
|
1070
|
+
noIndex: options.noIndex
|
|
845
1071
|
};
|
|
846
1072
|
for (const req of requirements) {
|
|
847
1073
|
if (req.type === "RequirementsFile") {
|
|
@@ -861,6 +1087,23 @@ function parseRequirementsFileInternal(fileContent, readFile3, visited) {
|
|
|
861
1087
|
normalized.push(norm);
|
|
862
1088
|
}
|
|
863
1089
|
}
|
|
1090
|
+
for (const rawPath of pathRequirements) {
|
|
1091
|
+
const norm = normalizePathRequirement(rawPath);
|
|
1092
|
+
if (norm != null) {
|
|
1093
|
+
normalized.push(norm);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
for (const rawPath of editableRequirements) {
|
|
1097
|
+
const norm = normalizePathRequirement(rawPath);
|
|
1098
|
+
if (norm != null) {
|
|
1099
|
+
if (norm.source) {
|
|
1100
|
+
norm.source.editable = true;
|
|
1101
|
+
} else {
|
|
1102
|
+
norm.source = { path: rawPath, editable: true };
|
|
1103
|
+
}
|
|
1104
|
+
normalized.push(norm);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
864
1107
|
if (readFile3) {
|
|
865
1108
|
for (const refPath of mergedOptions.requirementFiles) {
|
|
866
1109
|
const refPathKey = normalize(refPath);
|
|
@@ -897,6 +1140,18 @@ function parseRequirementsFileInternal(fileContent, readFile3, visited) {
|
|
|
897
1140
|
mergedOptions.constraintFiles.push(constraintPath);
|
|
898
1141
|
}
|
|
899
1142
|
}
|
|
1143
|
+
if (refParsed.pipOptions.findLinks) {
|
|
1144
|
+
if (!mergedOptions.findLinks)
|
|
1145
|
+
mergedOptions.findLinks = [];
|
|
1146
|
+
for (const fl of refParsed.pipOptions.findLinks) {
|
|
1147
|
+
if (!mergedOptions.findLinks.includes(fl)) {
|
|
1148
|
+
mergedOptions.findLinks.push(fl);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (refParsed.pipOptions.noIndex) {
|
|
1153
|
+
mergedOptions.noIndex = true;
|
|
1154
|
+
}
|
|
900
1155
|
}
|
|
901
1156
|
}
|
|
902
1157
|
}
|
|
@@ -1385,6 +1640,11 @@ var PythonManifestKind = /* @__PURE__ */ ((PythonManifestKind2) => {
|
|
|
1385
1640
|
PythonManifestKind2["PyProjectToml"] = "pyproject.toml";
|
|
1386
1641
|
return PythonManifestKind2;
|
|
1387
1642
|
})(PythonManifestKind || {});
|
|
1643
|
+
var PythonLockFileKind = /* @__PURE__ */ ((PythonLockFileKind2) => {
|
|
1644
|
+
PythonLockFileKind2["UvLock"] = "uv.lock";
|
|
1645
|
+
PythonLockFileKind2["PylockToml"] = "pylock.toml";
|
|
1646
|
+
return PythonLockFileKind2;
|
|
1647
|
+
})(PythonLockFileKind || {});
|
|
1388
1648
|
var PythonManifestConvertedKind = /* @__PURE__ */ ((PythonManifestConvertedKind2) => {
|
|
1389
1649
|
PythonManifestConvertedKind2["Pipfile"] = "Pipfile";
|
|
1390
1650
|
PythonManifestConvertedKind2["PipfileLock"] = "Pipfile.lock";
|
|
@@ -1426,6 +1686,7 @@ async function discoverPythonPackage({
|
|
|
1426
1686
|
}
|
|
1427
1687
|
let entrypointManifest;
|
|
1428
1688
|
let workspaceManifest;
|
|
1689
|
+
let workspaceLockFile;
|
|
1429
1690
|
if (manifests.length === 0) {
|
|
1430
1691
|
return {
|
|
1431
1692
|
configs
|
|
@@ -1437,6 +1698,7 @@ async function discoverPythonPackage({
|
|
|
1437
1698
|
manifests
|
|
1438
1699
|
);
|
|
1439
1700
|
workspaceManifest = entrypointWorkspaceManifest;
|
|
1701
|
+
workspaceLockFile = entrypointWorkspaceManifest.lockFile;
|
|
1440
1702
|
configs = configs.filter(
|
|
1441
1703
|
(config) => Object.values(config).some(
|
|
1442
1704
|
(cfg) => cfg !== void 0 && isSubpath(
|
|
@@ -1454,6 +1716,7 @@ async function discoverPythonPackage({
|
|
|
1454
1716
|
return {
|
|
1455
1717
|
manifest: entrypointManifest,
|
|
1456
1718
|
workspaceManifest,
|
|
1719
|
+
workspaceLockFile,
|
|
1457
1720
|
configs,
|
|
1458
1721
|
requiresPython
|
|
1459
1722
|
};
|
|
@@ -1568,6 +1831,21 @@ async function loadPythonManifest(root, prefix) {
|
|
|
1568
1831
|
}
|
|
1569
1832
|
return manifest;
|
|
1570
1833
|
}
|
|
1834
|
+
async function maybeLoadLockFile(root, subdir) {
|
|
1835
|
+
const uvLockRelPath = path3.join(subdir, "uv.lock");
|
|
1836
|
+
const uvLockPath = path3.join(root, uvLockRelPath);
|
|
1837
|
+
const uvLockContent = await readFileTextIfExists(uvLockPath);
|
|
1838
|
+
if (uvLockContent != null) {
|
|
1839
|
+
return { path: uvLockRelPath, kind: "uv.lock" /* UvLock */ };
|
|
1840
|
+
}
|
|
1841
|
+
const pylockRelPath = path3.join(subdir, "pylock.toml");
|
|
1842
|
+
const pylockPath = path3.join(root, pylockRelPath);
|
|
1843
|
+
const pylockContent = await readFileTextIfExists(pylockPath);
|
|
1844
|
+
if (pylockContent != null) {
|
|
1845
|
+
return { path: pylockRelPath, kind: "pylock.toml" /* PylockToml */ };
|
|
1846
|
+
}
|
|
1847
|
+
return void 0;
|
|
1848
|
+
}
|
|
1571
1849
|
async function maybeLoadPyProjectToml(root, subdir) {
|
|
1572
1850
|
const pyprojectTomlRelPath = path3.join(subdir, "pyproject.toml");
|
|
1573
1851
|
const pyprojectTomlPath = path3.join(root, pyprojectTomlRelPath);
|
|
@@ -1614,9 +1892,11 @@ async function maybeLoadPyProjectToml(root, subdir) {
|
|
|
1614
1892
|
pyproject.tool.uv = uvToml;
|
|
1615
1893
|
}
|
|
1616
1894
|
}
|
|
1895
|
+
const lockFile = await maybeLoadLockFile(root, subdir);
|
|
1617
1896
|
return {
|
|
1618
1897
|
path: pyprojectTomlRelPath,
|
|
1619
|
-
data: pyproject
|
|
1898
|
+
data: pyproject,
|
|
1899
|
+
lockFile
|
|
1620
1900
|
};
|
|
1621
1901
|
}
|
|
1622
1902
|
async function maybeLoadPipfile(root, subdir) {
|
|
@@ -1703,12 +1983,16 @@ async function maybeLoadRequirementsTxt(root, subdir, fileName) {
|
|
|
1703
1983
|
} catch (error) {
|
|
1704
1984
|
if (error instanceof PythonAnalysisError) {
|
|
1705
1985
|
error.path = requirementsTxtRelPath;
|
|
1986
|
+
if (!error.fileContent) {
|
|
1987
|
+
error.fileContent = requirementsContent;
|
|
1988
|
+
}
|
|
1706
1989
|
throw error;
|
|
1707
1990
|
}
|
|
1708
1991
|
throw new PythonAnalysisError({
|
|
1709
1992
|
message: `could not parse ${fileName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1710
1993
|
code: "PYTHON_REQUIREMENTS_PARSE_ERROR",
|
|
1711
|
-
path: requirementsTxtRelPath
|
|
1994
|
+
path: requirementsTxtRelPath,
|
|
1995
|
+
fileContent: requirementsContent
|
|
1712
1996
|
});
|
|
1713
1997
|
}
|
|
1714
1998
|
}
|
|
@@ -1745,6 +2029,29 @@ async function maybeLoadPythonRequest(root, subdir) {
|
|
|
1745
2029
|
};
|
|
1746
2030
|
}
|
|
1747
2031
|
|
|
2032
|
+
// src/manifest/serialize.ts
|
|
2033
|
+
import toml2 from "smol-toml";
|
|
2034
|
+
function stringifyManifest(data) {
|
|
2035
|
+
return toml2.stringify(data);
|
|
2036
|
+
}
|
|
2037
|
+
function createMinimalManifest(options = {}) {
|
|
2038
|
+
const {
|
|
2039
|
+
name = "app",
|
|
2040
|
+
version = "0.1.0",
|
|
2041
|
+
requiresPython,
|
|
2042
|
+
dependencies = []
|
|
2043
|
+
} = options;
|
|
2044
|
+
return {
|
|
2045
|
+
project: {
|
|
2046
|
+
name,
|
|
2047
|
+
version,
|
|
2048
|
+
...requiresPython && { "requires-python": requiresPython },
|
|
2049
|
+
dependencies,
|
|
2050
|
+
classifiers: ["Private :: Do Not Upload"]
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
|
|
1748
2055
|
// src/manifest/python-selector.ts
|
|
1749
2056
|
function selectPython(constraints, available) {
|
|
1750
2057
|
const warnings = [];
|
|
@@ -1917,6 +2224,7 @@ export {
|
|
|
1917
2224
|
PythonBuild,
|
|
1918
2225
|
PythonConfigKind,
|
|
1919
2226
|
PythonImplementation,
|
|
2227
|
+
PythonLockFileKind,
|
|
1920
2228
|
PythonManifestConvertedKind,
|
|
1921
2229
|
PythonManifestKind,
|
|
1922
2230
|
PythonVariant,
|
|
@@ -1927,6 +2235,8 @@ export {
|
|
|
1927
2235
|
UvConfigWorkspaceSchema,
|
|
1928
2236
|
UvIndexEntrySchema,
|
|
1929
2237
|
containsAppOrHandler,
|
|
2238
|
+
createMinimalManifest,
|
|
1930
2239
|
discoverPythonPackage,
|
|
1931
|
-
selectPython
|
|
2240
|
+
selectPython,
|
|
2241
|
+
stringifyManifest
|
|
1932
2242
|
};
|
|
@@ -20,6 +20,26 @@ export declare enum PythonManifestKind {
|
|
|
20
20
|
/** Standard `pyproject.toml` file (PEP 517/518/621). */
|
|
21
21
|
PyProjectToml = "pyproject.toml"
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Kinds of Python lock files.
|
|
25
|
+
*
|
|
26
|
+
* Lock files pin exact dependency versions for reproducible installs.
|
|
27
|
+
*/
|
|
28
|
+
export declare enum PythonLockFileKind {
|
|
29
|
+
/** uv's lock file format. */
|
|
30
|
+
UvLock = "uv.lock",
|
|
31
|
+
/** PEP 751 standard lock file format. */
|
|
32
|
+
PylockToml = "pylock.toml"
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Information about a detected lock file.
|
|
36
|
+
*/
|
|
37
|
+
export interface PythonLockFile {
|
|
38
|
+
/** Relative path to the lock file. */
|
|
39
|
+
path: RelPath;
|
|
40
|
+
/** The lock file format. */
|
|
41
|
+
kind: PythonLockFileKind;
|
|
42
|
+
}
|
|
23
43
|
/**
|
|
24
44
|
* Kinds of Python package manifests that are converted to pyproject.toml format.
|
|
25
45
|
*
|
|
@@ -63,6 +83,8 @@ export interface PythonManifest {
|
|
|
63
83
|
origin?: PythonManifestOrigin;
|
|
64
84
|
/** Whether this manifest represents a workspace root. */
|
|
65
85
|
isRoot?: boolean;
|
|
86
|
+
/** Lock file associated with this manifest, if one exists. */
|
|
87
|
+
lockFile?: PythonLockFile;
|
|
66
88
|
}
|
|
67
89
|
/**
|
|
68
90
|
* Configuration from a `.python-version` file.
|
|
@@ -130,6 +152,8 @@ export interface PythonPackage {
|
|
|
130
152
|
requiresPython?: PythonConstraint[];
|
|
131
153
|
/** The workspace root manifest, if this package is part of a workspace. */
|
|
132
154
|
workspaceManifest?: PythonManifest;
|
|
155
|
+
/** Lock file at the workspace root, if one exists. */
|
|
156
|
+
workspaceLockFile?: PythonLockFile;
|
|
133
157
|
}
|
|
134
158
|
/**
|
|
135
159
|
* Discover Python package information starting from an entrypoint directory.
|
|
@@ -219,16 +219,19 @@ export declare const pyProjectToolSectionSchema: z.ZodObject<{
|
|
|
219
219
|
url: z.ZodString;
|
|
220
220
|
default: z.ZodOptional<z.ZodBoolean>;
|
|
221
221
|
explicit: z.ZodOptional<z.ZodBoolean>;
|
|
222
|
+
format: z.ZodOptional<z.ZodString>;
|
|
222
223
|
}, "strip", z.ZodTypeAny, {
|
|
223
224
|
name: string;
|
|
224
225
|
url: string;
|
|
225
226
|
default?: boolean | undefined;
|
|
226
227
|
explicit?: boolean | undefined;
|
|
228
|
+
format?: string | undefined;
|
|
227
229
|
}, {
|
|
228
230
|
name: string;
|
|
229
231
|
url: string;
|
|
230
232
|
default?: boolean | undefined;
|
|
231
233
|
explicit?: boolean | undefined;
|
|
234
|
+
format?: string | undefined;
|
|
232
235
|
}>, "many">>;
|
|
233
236
|
workspace: z.ZodOptional<z.ZodObject<{
|
|
234
237
|
members: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -259,6 +262,7 @@ export declare const pyProjectToolSectionSchema: z.ZodObject<{
|
|
|
259
262
|
url: string;
|
|
260
263
|
default?: boolean | undefined;
|
|
261
264
|
explicit?: boolean | undefined;
|
|
265
|
+
format?: string | undefined;
|
|
262
266
|
}[] | undefined;
|
|
263
267
|
workspace?: {
|
|
264
268
|
members?: string[] | undefined;
|
|
@@ -283,6 +287,7 @@ export declare const pyProjectToolSectionSchema: z.ZodObject<{
|
|
|
283
287
|
url: string;
|
|
284
288
|
default?: boolean | undefined;
|
|
285
289
|
explicit?: boolean | undefined;
|
|
290
|
+
format?: string | undefined;
|
|
286
291
|
}[] | undefined;
|
|
287
292
|
workspace?: {
|
|
288
293
|
members?: string[] | undefined;
|
|
@@ -309,6 +314,7 @@ export declare const pyProjectToolSectionSchema: z.ZodObject<{
|
|
|
309
314
|
url: string;
|
|
310
315
|
default?: boolean | undefined;
|
|
311
316
|
explicit?: boolean | undefined;
|
|
317
|
+
format?: string | undefined;
|
|
312
318
|
}[] | undefined;
|
|
313
319
|
workspace?: {
|
|
314
320
|
members?: string[] | undefined;
|
|
@@ -335,6 +341,7 @@ export declare const pyProjectToolSectionSchema: z.ZodObject<{
|
|
|
335
341
|
url: string;
|
|
336
342
|
default?: boolean | undefined;
|
|
337
343
|
explicit?: boolean | undefined;
|
|
344
|
+
format?: string | undefined;
|
|
338
345
|
}[] | undefined;
|
|
339
346
|
workspace?: {
|
|
340
347
|
members?: string[] | undefined;
|
|
@@ -513,16 +520,19 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
513
520
|
url: z.ZodString;
|
|
514
521
|
default: z.ZodOptional<z.ZodBoolean>;
|
|
515
522
|
explicit: z.ZodOptional<z.ZodBoolean>;
|
|
523
|
+
format: z.ZodOptional<z.ZodString>;
|
|
516
524
|
}, "strip", z.ZodTypeAny, {
|
|
517
525
|
name: string;
|
|
518
526
|
url: string;
|
|
519
527
|
default?: boolean | undefined;
|
|
520
528
|
explicit?: boolean | undefined;
|
|
529
|
+
format?: string | undefined;
|
|
521
530
|
}, {
|
|
522
531
|
name: string;
|
|
523
532
|
url: string;
|
|
524
533
|
default?: boolean | undefined;
|
|
525
534
|
explicit?: boolean | undefined;
|
|
535
|
+
format?: string | undefined;
|
|
526
536
|
}>, "many">>;
|
|
527
537
|
workspace: z.ZodOptional<z.ZodObject<{
|
|
528
538
|
members: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -553,6 +563,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
553
563
|
url: string;
|
|
554
564
|
default?: boolean | undefined;
|
|
555
565
|
explicit?: boolean | undefined;
|
|
566
|
+
format?: string | undefined;
|
|
556
567
|
}[] | undefined;
|
|
557
568
|
workspace?: {
|
|
558
569
|
members?: string[] | undefined;
|
|
@@ -577,6 +588,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
577
588
|
url: string;
|
|
578
589
|
default?: boolean | undefined;
|
|
579
590
|
explicit?: boolean | undefined;
|
|
591
|
+
format?: string | undefined;
|
|
580
592
|
}[] | undefined;
|
|
581
593
|
workspace?: {
|
|
582
594
|
members?: string[] | undefined;
|
|
@@ -603,6 +615,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
603
615
|
url: string;
|
|
604
616
|
default?: boolean | undefined;
|
|
605
617
|
explicit?: boolean | undefined;
|
|
618
|
+
format?: string | undefined;
|
|
606
619
|
}[] | undefined;
|
|
607
620
|
workspace?: {
|
|
608
621
|
members?: string[] | undefined;
|
|
@@ -629,6 +642,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
629
642
|
url: string;
|
|
630
643
|
default?: boolean | undefined;
|
|
631
644
|
explicit?: boolean | undefined;
|
|
645
|
+
format?: string | undefined;
|
|
632
646
|
}[] | undefined;
|
|
633
647
|
workspace?: {
|
|
634
648
|
members?: string[] | undefined;
|
|
@@ -693,6 +707,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
693
707
|
url: string;
|
|
694
708
|
default?: boolean | undefined;
|
|
695
709
|
explicit?: boolean | undefined;
|
|
710
|
+
format?: string | undefined;
|
|
696
711
|
}[] | undefined;
|
|
697
712
|
workspace?: {
|
|
698
713
|
members?: string[] | undefined;
|
|
@@ -757,6 +772,7 @@ export declare const pyProjectTomlSchema: z.ZodObject<{
|
|
|
757
772
|
url: string;
|
|
758
773
|
default?: boolean | undefined;
|
|
759
774
|
explicit?: boolean | undefined;
|
|
775
|
+
format?: string | undefined;
|
|
760
776
|
}[] | undefined;
|
|
761
777
|
workspace?: {
|
|
762
778
|
members?: string[] | undefined;
|