Skip to content

Commit e743037

Browse files
authored
Ensure editable install only when [build-system] is present pyproject.toml (#20625)
Fixes #20620
1 parent b43bc25 commit e743037

File tree

2 files changed

+46
-18
lines changed

2 files changed

+46
-18
lines changed

src/client/pythonEnvironments/creation/provider/venvUtils.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { CancellationToken, QuickPickItem, RelativePattern, WorkspaceFolder } fr
99
import { CreateEnv } from '../../../common/utils/localize';
1010
import { showQuickPick } from '../../../common/vscodeApis/windowApis';
1111
import { findFiles } from '../../../common/vscodeApis/workspaceApis';
12-
import { traceError, traceVerbose } from '../../../logging';
12+
import { traceError, traceInfo, traceVerbose } from '../../../logging';
1313

1414
const exclude = '**/{.venv*,.git,.nox,.tox,.conda,site-packages,__pypackages__}/**';
1515
async function getPipRequirementsFiles(
@@ -25,20 +25,27 @@ async function getPipRequirementsFiles(
2525
return files;
2626
}
2727

28-
async function getTomlOptionalDeps(tomlPath: string): Promise<string[]> {
29-
const content = await fs.readFile(tomlPath, 'utf-8');
30-
const extras: string[] = [];
28+
function tomlParse(content: string): tomljs.JsonMap {
3129
try {
32-
const toml = tomljs.parse(content);
33-
if (toml.project && (toml.project as Record<string, Array<string>>)['optional-dependencies']) {
34-
const deps = (toml.project as Record<string, Record<string, Array<string>>>)['optional-dependencies'];
35-
for (const key of Object.keys(deps)) {
36-
extras.push(key);
37-
}
38-
}
30+
return tomljs.parse(content);
3931
} catch (err) {
4032
traceError('Failed to parse `pyproject.toml`:', err);
4133
}
34+
return {};
35+
}
36+
37+
function tomlHasBuildSystem(toml: tomljs.JsonMap): boolean {
38+
return toml['build-system'] !== undefined;
39+
}
40+
41+
function getTomlOptionalDeps(toml: tomljs.JsonMap): string[] {
42+
const extras: string[] = [];
43+
if (toml.project && (toml.project as tomljs.JsonMap)['optional-dependencies']) {
44+
const deps = (toml.project as tomljs.JsonMap)['optional-dependencies'];
45+
for (const key of Object.keys(deps)) {
46+
extras.push(key);
47+
}
48+
}
4249
return extras;
4350
}
4451

@@ -109,12 +116,15 @@ export async function pickPackagesToInstall(
109116

110117
let extras: string[] = [];
111118
let tomlExists = false;
119+
let hasBuildSystem = false;
112120
if (await fs.pathExists(tomlPath)) {
113121
tomlExists = true;
114-
extras = await getTomlOptionalDeps(tomlPath);
122+
const toml = tomlParse(await fs.readFile(tomlPath, 'utf-8'));
123+
extras = getTomlOptionalDeps(toml);
124+
hasBuildSystem = tomlHasBuildSystem(toml);
115125
}
116126

117-
if (tomlExists) {
127+
if (tomlExists && hasBuildSystem) {
118128
if (extras.length === 0) {
119129
return { installType: 'toml', installList: [], source: tomlPath };
120130
}
@@ -125,6 +135,9 @@ export async function pickPackagesToInstall(
125135
}
126136
return undefined;
127137
}
138+
if (tomlExists) {
139+
traceInfo('Create env: Found toml without optional dependencies or build system.');
140+
}
128141

129142
traceVerbose('Looking for pip requirements.');
130143
const requirementFiles = (await getPipRequirementsFiles(workspaceFolder, token))?.map((p) =>

src/test/pythonEnvironments/creation/provider/venvUtils.unit.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,26 @@ suite('Venv Utils test', () => {
4646
});
4747
});
4848

49-
test('Toml found with no optional deps', async () => {
49+
test('Toml found with no build system', async () => {
5050
findFilesStub.resolves([]);
5151
pathExistsStub.resolves(true);
5252
readFileStub.resolves('[project]\nname = "spam"\nversion = "2020.0.0"\n');
5353

54+
const actual = await pickPackagesToInstall(workspace1);
55+
assert.isTrue(showQuickPickStub.notCalled);
56+
assert.deepStrictEqual(actual, {
57+
installType: 'none',
58+
installList: [],
59+
});
60+
});
61+
62+
test('Toml found with no optional deps', async () => {
63+
findFilesStub.resolves([]);
64+
pathExistsStub.resolves(true);
65+
readFileStub.resolves(
66+
'[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]',
67+
);
68+
5469
const actual = await pickPackagesToInstall(workspace1);
5570
assert.isTrue(showQuickPickStub.notCalled);
5671
assert.deepStrictEqual(actual, {
@@ -64,7 +79,7 @@ suite('Venv Utils test', () => {
6479
findFilesStub.resolves([]);
6580
pathExistsStub.resolves(true);
6681
readFileStub.resolves(
67-
'[project]\nname = "spam"\nversion = "2020.0.0"\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
82+
'[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
6883
);
6984

7085
showQuickPickStub.resolves(undefined);
@@ -88,7 +103,7 @@ suite('Venv Utils test', () => {
88103
findFilesStub.resolves([]);
89104
pathExistsStub.resolves(true);
90105
readFileStub.resolves(
91-
'[project]\nname = "spam"\nversion = "2020.0.0"\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
106+
'[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
92107
);
93108

94109
showQuickPickStub.resolves([]);
@@ -116,7 +131,7 @@ suite('Venv Utils test', () => {
116131
findFilesStub.resolves([]);
117132
pathExistsStub.resolves(true);
118133
readFileStub.resolves(
119-
'[project]\nname = "spam"\nversion = "2020.0.0"\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
134+
'[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]',
120135
);
121136

122137
showQuickPickStub.resolves([{ label: 'doc' }]);
@@ -144,7 +159,7 @@ suite('Venv Utils test', () => {
144159
findFilesStub.resolves([]);
145160
pathExistsStub.resolves(true);
146161
readFileStub.resolves(
147-
'[project]\nname = "spam"\nversion = "2020.0.0"\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]\ncov = ["pytest-cov"]',
162+
'[project]\nname = "spam"\nversion = "2020.0.0"\n[build-system]\nrequires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]\n[project.optional-dependencies]\ntest = ["pytest"]\ndoc = ["sphinx", "furo"]\ncov = ["pytest-cov"]',
148163
);
149164

150165
showQuickPickStub.resolves([{ label: 'test' }, { label: 'cov' }]);

0 commit comments

Comments
 (0)