@@ -13,86 +13,49 @@ import { Installable } from '../common/types';
1313import { mergePackages } from '../common/utils' ;
1414import { refreshPipPackages } from './utils' ;
1515
16- /**
17- * Validates pyproject.toml fields according to PEP 508, PEP 440, PEP 621, PEP 517/518
18- * Returns error message if invalid, undefined if valid
19- */
20- function validatePyprojectToml ( toml : tomljs . JsonMap , filePath : string ) : string | undefined {
16+ function validatePyprojectToml ( toml : tomljs . JsonMap ) : string | undefined {
2117 // 1. Validate package name (PEP 508)
2218 if ( toml . project && ( toml . project as tomljs . JsonMap ) . name ) {
2319 const name = ( toml . project as tomljs . JsonMap ) . name as string ;
24- // PEP 508 regex: must start and end with a letter or digit, can contain -_.
20+ // PEP 508 regex: must start and end with a letter or digit, can contain -_., and alphanumeric characters. No spaces allowed.
21+ // See https://peps.python.org/pep-0508/
2522 const nameRegex = / ^ ( [ a - z A - Z 0 - 9 ] | [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 . _ - ] * [ a - z A - Z 0 - 9 ] ) $ / ;
2623 if ( ! nameRegex . test ( name ) ) {
27- return l10n . t (
28- 'Invalid package name "{0}" in {1}. Package names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters. No spaces allowed. See PEP 508: https://peps.python.org/pep-0508/' ,
29- name ,
30- path . basename ( filePath ) ,
31- ) ;
24+ return l10n . t ( 'Invalid package name "{0}" in pyproject.toml.' , name ) ;
3225 }
3326 }
3427
3528 // 2. Validate version format (PEP 440)
3629 if ( toml . project && ( toml . project as tomljs . JsonMap ) . version ) {
3730 const version = ( toml . project as tomljs . JsonMap ) . version as string ;
38- // PEP 440 simplified regex
31+ // PEP 440 version regex. Versions must follow PEP 440 format (e.g., "1.0.0", "2.1a3").
32+ // See https://peps.python.org/pep-0440/
3933 const versionRegex =
40- / ^ ( [ 1 - 9 ] [ 0 - 9 ] * ! ) ? ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ( \. ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ) * ( ( a | b | r c ) ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ) ? ( \. p o s t ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ) ? ( \. d e v ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ) ? $ / ;
34+ / ^ ( [ 0 - 9 ] + ! ) ? ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ( \. ( 0 | [ 1 - 9 ] [ 0 - 9 ] * ) ) * ( ( a | b | c | r c ) ( [ 0 - 9 ] + ) ? ) ? ( \. p o s t ( [ 0 - 9 ] + ) ? ) ? ( \. d e v ( [ 0 - 9 ] + ) ? ) ? ( \+ [ a - z A - Z 0 - 9 . _ - ] + ) ? $ / ;
4135 if ( ! versionRegex . test ( version ) ) {
42- return l10n . t (
43- 'Invalid version "{0}" in {1}. Versions must follow PEP 440 format (e.g., "1.0.0", "2.1a3"). See https://peps.python.org/pep-0440/' ,
44- version ,
45- path . basename ( filePath ) ,
46- ) ;
36+ return l10n . t ( 'Invalid version "{0}" in pyproject.toml.' , version ) ;
4737 }
4838 }
4939
5040 // 3. Validate required fields (PEP 621)
5141 if ( toml . project ) {
5242 const project = toml . project as tomljs . JsonMap ;
43+ // See PEP 621: https://peps.python.org/pep-0621/
5344 if ( ! project . name ) {
54- return l10n . t (
55- 'Missing required field "name" in [project] section of {0}. See PEP 621: https://peps.python.org/pep-0621/' ,
56- path . basename ( filePath ) ,
57- ) ;
45+ return l10n . t ( 'Missing required field "name" in [project] section of pyproject.toml.' ) ;
5846 }
5947 }
6048
61- // 4. Validate build system (PEP 517/ 518)
49+ // 4. Validate build system (PEP 518)
6250 if ( toml [ 'build-system' ] ) {
6351 const buildSystem = toml [ 'build-system' ] as tomljs . JsonMap ;
52+ // See PEP 518: https://peps.python.org/pep-0518/
6453 if ( ! buildSystem . requires ) {
65- return l10n . t (
66- 'Missing required field "requires" in [build-system] section of {0}. See PEP 517: https://peps.python.org/pep-0517/' ,
67- path . basename ( filePath ) ,
68- ) ;
69- }
70- if ( ! buildSystem [ 'build-backend' ] ) {
71- return l10n . t (
72- 'Missing required field "build-backend" in [build-system] section of {0}. See PEP 518: https://peps.python.org/pep-0518/' ,
73- path . basename ( filePath ) ,
74- ) ;
54+ return l10n . t ( 'Missing required field "requires" in [build-system] section of pyproject.toml.' ) ;
7555 }
7656 }
7757
78- // 5. Validate dependencies format (PEP 508)
79- if ( toml . project && ( toml . project as tomljs . JsonMap ) . dependencies ) {
80- const deps = ( toml . project as tomljs . JsonMap ) . dependencies as string [ ] ;
81- if ( Array . isArray ( deps ) ) {
82- for ( const dep of deps ) {
83- // Basic check for common mistakes
84- if ( dep . includes ( ' ' ) || / \s { 2 , } / . test ( dep ) ) {
85- return l10n . t (
86- 'Invalid dependency "{0}" in {1}. Contains extra whitespace. See PEP 508: https://peps.python.org/pep-0508/' ,
87- dep ,
88- path . basename ( filePath ) ,
89- ) ;
90- }
91- }
92- }
93- }
94-
95- return undefined ; // No errors
58+ return undefined ;
9659}
9760
9861async function tomlParse ( fsPath : string , log ?: LogOutputChannel ) : Promise < tomljs . JsonMap > {
@@ -329,7 +292,7 @@ export async function getProjectInstallable(
329292
330293 // Validate pyproject.toml and capture first error only
331294 if ( ! validationError ) {
332- const error = validatePyprojectToml ( toml , uri . fsPath ) ;
295+ const error = validatePyprojectToml ( toml ) ;
333296 if ( error ) {
334297 validationError = {
335298 message : error ,
@@ -380,7 +343,12 @@ export async function shouldProceedAfterPyprojectValidation(
380343 const continueButton = { title : Pickers . pyProject . continueAnyway } ;
381344 const cancelButton = { title : Pickers . pyProject . cancel , isCloseAffordance : true } ;
382345
383- const selection = await window . showErrorMessage ( validationError . message , openButton , continueButton , cancelButton ) ;
346+ const selection = await window . showErrorMessage (
347+ validationError . message + Pickers . pyProject . validationErrorAction ,
348+ openButton ,
349+ continueButton ,
350+ cancelButton ,
351+ ) ;
384352
385353 if ( selection === continueButton ) {
386354 return true ;
0 commit comments