Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

install python modules in Terminal when python.installModulesInTerminal enabled #5529

Merged
merged 7 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extensions/positron-python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,12 @@
"description": "%python.venvPath.description%",
"scope": "machine",
"type": "string"
},
"python.installModulesInTerminal": {
"default": false,
"markdownDescription": "%python.installModulesInTerminal.description%",
"scope": "resource",
"type": "boolean"
}
},
"title": "Python",
Expand Down
1 change: 1 addition & 0 deletions extensions/positron-python/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"python.testing.unittestEnabled.description": "Enable testing using unittest.",
"python.venvFolders.description": "Folders in your home directory to look into for virtual environments (supports pyenv, direnv and virtualenvwrapper by default).",
"python.venvPath.description": "Path to folder with a list of Virtual Environments (e.g. ~/.pyenv, ~/Envs, ~/.virtualenvs).",
"python.installModulesInTerminal.description": "Whether to install Python modules (such as `ipykernel`) in the Terminal, instead of in a background process. Installing modules in the Terminal allows you to see the output of the installation command.",
"walkthrough.pythonWelcome.title": "Get Started with Python Development",
"walkthrough.pythonWelcome.description": "Your first steps to set up a Python project with all the powerful tools and features that the Python extension has to offer!",
"walkthrough.step.python.createPythonFile.title": "Create a Python file",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// --- Start Positron ---
/* eslint-disable max-classes-per-file, import/no-duplicates */
import { CancellationTokenSource } from 'vscode';
// --- End Positron ---

import { injectable } from 'inversify';
import * as path from 'path';
import { CancellationToken, l10n, ProgressLocation, ProgressOptions } from 'vscode';
Expand All @@ -22,6 +27,8 @@ import { ProductNames } from './productNames';
import { IModuleInstaller, InstallOptions, InterpreterUri, ModuleInstallFlags } from './types';

// --- Start Positron ---
// eslint-disable-next-line import/newline-after-import
import { IWorkspaceService } from '../application/types';
class ExternallyManagedEnvironmentError extends Error {}
// --- End Positron ---

Expand All @@ -44,7 +51,9 @@ export abstract class ModuleInstaller implements IModuleInstaller {
flags?: ModuleInstallFlags,
options?: InstallOptions,
): Promise<void> {
const shouldExecuteInTerminal = !options?.installAsProcess;
// --- Start Positron ---
const shouldExecuteInTerminal = this.installModulesInTerminal() || !options?.installAsProcess;
// --- End Positron ---
const name =
typeof productOrModuleName === 'string'
? productOrModuleName
Expand Down Expand Up @@ -249,6 +258,18 @@ export abstract class ModuleInstaller implements IModuleInstaller {
.get<ITerminalServiceFactory>(ITerminalServiceFactory)
.getTerminalService(options);

// --- Start Positron ---
// When running with the `python.installModulesInTerminal` setting enabled, we want to
// ensure that the terminal command is fully executed before returning. Otherwise, the
// calling code of the install will not be able to tell when the installation is complete.
if (this.installModulesInTerminal()) {
// Ensure we pass a cancellation token so that we await the full terminal command
// execution before returning.
const cancelToken = token ?? new CancellationTokenSource().token;
await terminalService.sendCommand(command, args, token ?? cancelToken);
return;
}
// --- End Positron ---
terminalService.sendCommand(command, args, token);
} else {
const processServiceFactory = this.serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
Expand Down Expand Up @@ -278,6 +299,22 @@ export abstract class ModuleInstaller implements IModuleInstaller {
// --- End Positron ---
}
}

// --- Start Positron ---
/**
* Check if the user has enabled the setting to install modules in the terminal.
*
* `python.installModulesInTerminal` is a setting that allows the user to force modules to be
* installed in the Terminal. Usually, such installations occur in the background. However,
* for debugging, it can be helpful to see the Terminal output of the installation process.
* @returns `true` if the user has enabled the setting to install modules in the Terminal,
* `false` if the user has disabled the setting, and `undefined` if the setting is not found.
*/
private installModulesInTerminal(): boolean | undefined {
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
return workspaceService.getConfiguration('python').get<boolean>('installModulesInTerminal');
}
// --- End Positron ---
}

export function translateProductToModule(product: Product): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ suite('Module Installer', () => {
workspaceService
.setup((w) => w.getConfiguration(TypeMoq.It.isValue('http')))
.returns(() => http.object);
// --- Start Positron ---
const pythonConfig = TypeMoq.Mock.ofType<WorkspaceConfiguration>();
pythonConfig
.setup((p) => p.get(TypeMoq.It.isValue('installModulesInTerminal'), TypeMoq.It.isAny()))
.returns(() => false);
workspaceService
.setup((w) => w.getConfiguration(TypeMoq.It.isValue('python')))
.returns(() => pythonConfig.object);
// --- End Positron ---
installer = new InstallerClass(serviceContainer.object);
});
teardown(() => {
Expand Down
Loading