Skip to content

Commit d277639

Browse files
committed
improve error messages
1 parent d348717 commit d277639

2 files changed

Lines changed: 22 additions & 57 deletions

File tree

src/common/localize.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ export namespace Pickers {
6666
}
6767

6868
export namespace pyProject {
69-
export const validationError = l10n.t('Invalid pyproject.toml');
70-
export const validationErrorAction = l10n.t(
71-
'The pyproject.toml file has formatting errors. What would you like to do?',
72-
);
69+
export const validationErrorAction = l10n.t(' What would you like to do?');
7370
export const openFile = l10n.t('Open pyproject.toml');
7471
export const continueAnyway = l10n.t('Continue Anyway');
7572
export const cancel = l10n.t('Cancel');

src/managers/builtin/pipUtils.ts

Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,86 +13,49 @@ import { Installable } from '../common/types';
1313
import { mergePackages } from '../common/utils';
1414
import { 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-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-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|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$/;
34+
/^([0-9]+!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|c|rc)([0-9]+)?)?(\.post([0-9]+)?)?(\.dev([0-9]+)?)?(\+[a-zA-Z0-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

9861
async 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

Comments
 (0)