| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b37f277 commit 048897c
22 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + Added ability to wait for completion of the installation of modules. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -393,5 +393,6 @@ | |||
| 393 | 393 | "DataScience.fallbackToUseActiveInterpeterAsKernel": "Couldn't find kernel '{0}' that the notebook was created with. Using the current interpreter.", | |
| 394 | 394 | "DataScience.fallBackToRegisterAndUseActiveInterpeterAsKernel": "Couldn't find kernel '{0}' that the notebook was created with. Registering a new kernel using the current interpreter.", | |
| 395 | 395 | "DataScience.fallBackToPromptToUseActiveInterpreterOrSelectAKernel": "Couldn't find kernel '{0}' that the notebook was created with.", | |
| 396 | - "DataScience.kernelDescriptionForKernelPicker": "(kernel)" | ||
| 396 | + "DataScience.kernelDescriptionForKernelPicker": "(kernel)", | ||
| 397 | + "products.installingModule": "Installing {0}" | ||
| 397 | 398 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,36 @@ | |||
| 1 | + # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| 2 | + # Licensed under the MIT License. | ||
| 3 | + | ||
| 4 | + import os | ||
| 5 | + import sys | ||
| 6 | + import subprocess | ||
| 7 | + | ||
| 8 | + # This is a simple solution to waiting for completion of commands sent to terminal. | ||
| 9 | + # 1. Intercept commands send to a terminal | ||
| 10 | + # 2. Send commands to our script file with an additional argument | ||
| 11 | + # 3. In here create a file that'll log the progress. | ||
| 12 | + # 4. Calling code monitors the contents of the file to determine state of execution. | ||
| 13 | + | ||
| 14 | + # Last argument is a file that's used for synchronizing the actions in the terminal with the calling code in extension. | ||
| 15 | + lock_file = sys.argv[-1] | ||
| 16 | + shell_args = sys.argv[1:-1] | ||
| 17 | + | ||
| 18 | + print('Executing command in shell >> ' + ' '.join(shell_args)) | ||
| 19 | + | ||
| 20 | + with open(lock_file, 'w') as fp: | ||
| 21 | + try: | ||
| 22 | + # Signal start of execution. | ||
| 23 | + fp.write('START\n') | ||
| 24 | + fp.flush() | ||
| 25 | + | ||
| 26 | + subprocess.check_call(shell_args, stdout=sys.stdout, stderr=sys.stderr) | ||
| 27 | + | ||
| 28 | + # Signal start of execution. | ||
| 29 | + fp.write('END\n') | ||
| 30 | + fp.flush() | ||
| 31 | + except Exception: | ||
| 32 | + import traceback | ||
| 33 | + print(traceback.format_exc()) | ||
| 34 | + # Signal end of execution with failure state. | ||
| 35 | + fp.write('FAIL\n') | ||
| 36 | + fp.flush() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,13 +8,13 @@ import { IServiceContainer } from '../../ioc/types'; | |||
| 8 | 8 | import { ExecutionInfo, IConfigurationService } from '../types'; | |
| 9 | 9 | import { isResource } from '../utils/misc'; | |
| 10 | 10 | import { ModuleInstaller } from './moduleInstaller'; | |
| 11 | - import { IModuleInstaller, InterpreterUri } from './types'; | ||
| 11 | + import { InterpreterUri } from './types'; | ||
| 12 | 12 | ||
| 13 | 13 | /** | |
| 14 | 14 | * A Python module installer for a conda environment. | |
| 15 | 15 | */ | |
| 16 | 16 | @injectable() | |
| 17 | - export class CondaInstaller extends ModuleInstaller implements IModuleInstaller { | ||
| 17 | + export class CondaInstaller extends ModuleInstaller { | ||
| 18 | 18 | private isCondaAvailable: boolean | undefined; | |
| 19 | 19 | ||
| 20 | 20 | constructor( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,50 +4,70 @@ | |||
| 4 | 4 | import * as fs from 'fs'; | |
| 5 | 5 | import { injectable } from 'inversify'; | |
| 6 | 6 | import * as path from 'path'; | |
| 7 | - import { OutputChannel, window } from 'vscode'; | ||
| 7 | + import { CancellationToken, OutputChannel, ProgressLocation, ProgressOptions, window } from 'vscode'; | ||
| 8 | 8 | import { IInterpreterService, InterpreterType } from '../../interpreter/contracts'; | |
| 9 | 9 | import { IServiceContainer } from '../../ioc/types'; | |
| 10 | 10 | import { sendTelemetryEvent } from '../../telemetry'; | |
| 11 | 11 | import { EventName } from '../../telemetry/constants'; | |
| 12 | + import { IApplicationShell } from '../application/types'; | ||
| 13 | + import { wrapCancellationTokens } from '../cancellation'; | ||
| 12 | 14 | import { STANDARD_OUTPUT_CHANNEL } from '../constants'; | |
| 13 | 15 | import { ITerminalServiceFactory } from '../terminal/types'; | |
| 14 | 16 | import { ExecutionInfo, IConfigurationService, IOutputChannel } from '../types'; | |
| 17 | + import { Products } from '../utils/localize'; | ||
| 15 | 18 | import { isResource, noop } from '../utils/misc'; | |
| 16 | - import { InterpreterUri } from './types'; | ||
| 19 | + import { IModuleInstaller, InterpreterUri } from './types'; | ||
| 17 | 20 | ||
| 18 | 21 | @injectable() | |
| 19 | - export abstract class ModuleInstaller { | ||
| 22 | + export abstract class ModuleInstaller implements IModuleInstaller { | ||
| 23 | + public abstract get priority(): number; | ||
| 20 | 24 | public abstract get name(): string; | |
| 21 | 25 | public abstract get displayName(): string | |
| 22 | 26 | constructor(protected serviceContainer: IServiceContainer) { } | |
| 23 | - public async installModule(name: string, resource?: InterpreterUri): Promise<void> { | ||
| 27 | + public async installModule(name: string, resource?: InterpreterUri, cancel?: CancellationToken): Promise<void> { | ||
| 24 | 28 | sendTelemetryEvent(EventName.PYTHON_INSTALL_PACKAGE, undefined, { installer: this.displayName }); | |
| 25 | 29 | const uri = isResource(resource) ? resource : undefined; | |
| 26 | 30 | const executionInfo = await this.getExecutionInfo(name, resource); | |
| 27 | 31 | const terminalService = this.serviceContainer.get<ITerminalServiceFactory>(ITerminalServiceFactory).getTerminalService(uri); | |
| 32 | + const install = async (token?: CancellationToken) => { | ||
| 33 | + const executionInfoArgs = await this.processInstallArgs(executionInfo.args, resource); | ||
| 34 | + if (executionInfo.moduleName) { | ||
| 35 | + const configService = this.serviceContainer.get<IConfigurationService>(IConfigurationService); | ||
| 36 | + const settings = configService.getSettings(uri); | ||
| 37 | + const args = ['-m', executionInfo.moduleName].concat(executionInfoArgs); | ||
| 28 | 38 | ||
| 29 | - const executionInfoArgs = await this.processInstallArgs(executionInfo.args, resource); | ||
| 30 | - if (executionInfo.moduleName) { | ||
| 31 | - const configService = this.serviceContainer.get<IConfigurationService>(IConfigurationService); | ||
| 32 | - const settings = configService.getSettings(uri); | ||
| 33 | - const args = ['-m', executionInfo.moduleName].concat(executionInfoArgs); | ||
| 34 | - | ||
| 35 | - const interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService); | ||
| 36 | - const interpreter = isResource(resource) ? await interpreterService.getActiveInterpreter(resource) : resource; | ||
| 37 | - const pythonPath = isResource(resource) ? settings.pythonPath : resource.path; | ||
| 38 | - if (!interpreter || interpreter.type !== InterpreterType.Unknown) { | ||
| 39 | - await terminalService.sendCommand(pythonPath, args); | ||
| 40 | - } else if (settings.globalModuleInstallation) { | ||
| 41 | - if (await this.isPathWritableAsync(path.dirname(pythonPath))) { | ||
| 42 | - await terminalService.sendCommand(pythonPath, args); | ||
| 39 | + const interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService); | ||
| 40 | + const interpreter = isResource(resource) ? await interpreterService.getActiveInterpreter(resource) : resource; | ||
| 41 | + const pythonPath = isResource(resource) ? settings.pythonPath : resource.path; | ||
| 42 | + if (!interpreter || interpreter.type !== InterpreterType.Unknown) { | ||
| 43 | + await terminalService.sendCommand(pythonPath, args, token); | ||
| 44 | + } else if (settings.globalModuleInstallation) { | ||
| 45 | + if (await this.isPathWritableAsync(path.dirname(pythonPath))) { | ||
| 46 | + await terminalService.sendCommand(pythonPath, args, token); | ||
| 47 | + } else { | ||
| 48 | + this.elevatedInstall(pythonPath, args); | ||
| 49 | + } | ||
| 43 | 50 | } else { | |
| 44 | - this.elevatedInstall(pythonPath, args); | ||
| 51 | + await terminalService.sendCommand(pythonPath, args.concat(['--user']), token); | ||
| 45 | 52 | } | |
| 46 | 53 | } else { | |
| 47 | - await terminalService.sendCommand(pythonPath, args.concat(['--user'])); | ||
| 54 | + await terminalService.sendCommand(executionInfo.execPath!, executionInfoArgs, token); | ||
| 48 | 55 | } | |
| 56 | + }; | ||
| 57 | + | ||
| 58 | + // Display progress indicator if we have ability to cancel this operation from calling code. | ||
| 59 | + // This is required as its possible the installation can take a long time. | ||
| 60 | + // (i.e. if installation takes a long time in terminal or like, a progress indicator is necessary to let user know what is being waited on). | ||
| 61 | + if (cancel) { | ||
| 62 | + const shell = this.serviceContainer.get<IApplicationShell>(IApplicationShell); | ||
| 63 | + const options: ProgressOptions = { | ||
| 64 | + location: ProgressLocation.Notification, | ||
| 65 | + cancellable: true, | ||
| 66 | + title: Products.installingModule().format(name) | ||
| 67 | + }; | ||
| 68 | + await shell.withProgress(options, async (_, token: CancellationToken) => install(wrapCancellationTokens(token, cancel))); | ||
| 49 | 69 | } else { | |
| 50 | - await terminalService.sendCommand(executionInfo.execPath!, executionInfoArgs); | ||
| 70 | + await install(cancel); | ||
| 51 | 71 | } | |
| 52 | 72 | } | |
| 53 | 73 | public abstract isSupported(resource?: InterpreterUri): Promise<boolean>; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,12 +7,12 @@ import { IServiceContainer } from '../../ioc/types'; | |||
| 7 | 7 | import { ExecutionInfo } from '../types'; | |
| 8 | 8 | import { isResource } from '../utils/misc'; | |
| 9 | 9 | import { ModuleInstaller } from './moduleInstaller'; | |
| 10 | - import { IModuleInstaller, InterpreterUri } from './types'; | ||
| 10 | + import { InterpreterUri } from './types'; | ||
| 11 | 11 | ||
| 12 | 12 | export const pipenvName = 'pipenv'; | |
| 13 | 13 | ||
| 14 | 14 | @injectable() | |
| 15 | - export class PipEnvInstaller extends ModuleInstaller implements IModuleInstaller { | ||
| 15 | + export class PipEnvInstaller extends ModuleInstaller { | ||
| 16 | 16 | private readonly pipenv: IInterpreterLocatorService; | |
| 17 | 17 | ||
| 18 | 18 | public get name(): string { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,10 +8,10 @@ import { IPythonExecutionFactory } from '../process/types'; | |||
| 8 | 8 | import { ExecutionInfo } from '../types'; | |
| 9 | 9 | import { isResource } from '../utils/misc'; | |
| 10 | 10 | import { ModuleInstaller } from './moduleInstaller'; | |
| 11 | - import { IModuleInstaller, InterpreterUri } from './types'; | ||
| 11 | + import { InterpreterUri } from './types'; | ||
| 12 | 12 | ||
| 13 | 13 | @injectable() | |
| 14 | - export class PipInstaller extends ModuleInstaller implements IModuleInstaller { | ||
| 14 | + export class PipInstaller extends ModuleInstaller { | ||
| 15 | 15 | public get name(): string { | |
| 16 | 16 | return 'Pip'; | |
| 17 | 17 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,12 +14,12 @@ import { IProcessServiceFactory } from '../process/types'; | |||
| 14 | 14 | import { ExecutionInfo, IConfigurationService } from '../types'; | |
| 15 | 15 | import { isResource } from '../utils/misc'; | |
| 16 | 16 | import { ModuleInstaller } from './moduleInstaller'; | |
| 17 | - import { IModuleInstaller, InterpreterUri } from './types'; | ||
| 17 | + import { InterpreterUri } from './types'; | ||
| 18 | 18 | export const poetryName = 'poetry'; | |
| 19 | 19 | const poetryFile = 'poetry.lock'; | |
| 20 | 20 | ||
| 21 | 21 | @injectable() | |
| 22 | - export class PoetryInstaller extends ModuleInstaller implements IModuleInstaller { | ||
| 22 | + export class PoetryInstaller extends ModuleInstaller { | ||
| 23 | 23 | ||
| 24 | 24 | public get name(): string { | |
| 25 | 25 | return 'poetry'; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | import { inject, injectable, named } from 'inversify'; | |
| 4 | 4 | import * as os from 'os'; | |
| 5 | - import { OutputChannel, Uri } from 'vscode'; | ||
| 5 | + import { CancellationToken, OutputChannel, Uri } from 'vscode'; | ||
| 6 | 6 | import '../../common/extensions'; | |
| 7 | 7 | import * as localize from '../../common/utils/localize'; | |
| 8 | 8 | import { IServiceContainer } from '../../ioc/types'; | |
@@ -40,7 +40,7 @@ export abstract class BaseInstaller { | |||
| 40 | 40 | this.productService = serviceContainer.get<IProductService>(IProductService); | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | - public promptToInstall(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { | ||
| 43 | + public promptToInstall(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 44 | 44 | // If this method gets called twice, while previous promise has not been resolved, then return that same promise. | |
| 45 | 45 | // E.g. previous promise is not resolved as a message has been displayed to the user, so no point displaying | |
| 46 | 46 | // another message. | |
@@ -49,15 +49,15 @@ export abstract class BaseInstaller { | |||
| 49 | 49 | if (BaseInstaller.PromptPromises.has(key)) { | |
| 50 | 50 | return BaseInstaller.PromptPromises.get(key)!; | |
| 51 | 51 | } | |
| 52 | - const promise = this.promptToInstallImplementation(product, resource); | ||
| 52 | + const promise = this.promptToInstallImplementation(product, resource, cancel); | ||
| 53 | 53 | BaseInstaller.PromptPromises.set(key, promise); | |
| 54 | 54 | promise.then(() => BaseInstaller.PromptPromises.delete(key)).ignoreErrors(); | |
| 55 | 55 | promise.catch(() => BaseInstaller.PromptPromises.delete(key)).ignoreErrors(); | |
| 56 | 56 | ||
| 57 | 57 | return promise; | |
| 58 | 58 | } | |
| 59 | 59 | ||
| 60 | - public async install(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { | ||
| 60 | + public async install(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 61 | 61 | if (product === Product.unittest) { | |
| 62 | 62 | return InstallerResponse.Installed; | |
| 63 | 63 | } | |
@@ -70,7 +70,7 @@ export abstract class BaseInstaller { | |||
| 70 | 70 | ||
| 71 | 71 | const moduleName = translateProductToModule(product, ModuleNamePurpose.install); | |
| 72 | 72 | const logger = this.serviceContainer.get<ILogger>(ILogger); | |
| 73 | - await installer.installModule(moduleName, resource) | ||
| 73 | + await installer.installModule(moduleName, resource, cancel) | ||
| 74 | 74 | .catch(logger.logError.bind(logger, `Error in installing the module '${moduleName}'`)); | |
| 75 | 75 | ||
| 76 | 76 | return this.isInstalled(product, resource) | |
@@ -98,7 +98,7 @@ export abstract class BaseInstaller { | |||
| 98 | 98 | } | |
| 99 | 99 | } | |
| 100 | 100 | ||
| 101 | - protected abstract promptToInstallImplementation(product: Product, resource?: InterpreterUri): Promise<InstallerResponse>; | ||
| 101 | + protected abstract promptToInstallImplementation(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse>; | ||
| 102 | 102 | protected getExecutableNameFromSettings(product: Product, resource?: Uri): string { | |
| 103 | 103 | const productType = this.productService.getProductType(product); | |
| 104 | 104 | const productPathService = this.serviceContainer.get<IProductPathService>(IProductPathService, productType); | |
@@ -132,14 +132,14 @@ export class CTagsInstaller extends BaseInstaller { | |||
| 132 | 132 | } | |
| 133 | 133 | return InstallerResponse.Ignore; | |
| 134 | 134 | } | |
| 135 | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { | ||
| 135 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, _cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 136 | 136 | const item = await this.appShell.showErrorMessage('Install CTags to enable Python workspace symbols?', 'Yes', 'No'); | |
| 137 | 137 | return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; | |
| 138 | 138 | } | |
| 139 | 139 | } | |
| 140 | 140 | ||
| 141 | 141 | export class FormatterInstaller extends BaseInstaller { | |
| 142 | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { | ||
| 142 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 143 | 143 | // Hard-coded on purpose because the UI won't necessarily work having | |
| 144 | 144 | // another formatter. | |
| 145 | 145 | const formatters = [Product.autopep8, Product.black, Product.yapf]; | |
@@ -160,14 +160,14 @@ export class FormatterInstaller extends BaseInstaller { | |||
| 160 | 160 | ||
| 161 | 161 | const item = await this.appShell.showErrorMessage(message, ...options); | |
| 162 | 162 | if (item === yesChoice) { | |
| 163 | - return this.install(product, resource); | ||
| 163 | + return this.install(product, resource, cancel); | ||
| 164 | 164 | } else if (typeof item === 'string') { | |
| 165 | 165 | for (const formatter of formatters) { | |
| 166 | 166 | const formatterName = ProductNames.get(formatter)!; | |
| 167 | 167 | ||
| 168 | 168 | if (item.endsWith(formatterName)) { | |
| 169 | 169 | await this.configService.updateSetting('formatting.provider', formatterName, resource); | |
| 170 | - return this.install(formatter, resource); | ||
| 170 | + return this.install(formatter, resource, cancel); | ||
| 171 | 171 | } | |
| 172 | 172 | } | |
| 173 | 173 | } | |
@@ -177,7 +177,7 @@ export class FormatterInstaller extends BaseInstaller { | |||
| 177 | 177 | } | |
| 178 | 178 | ||
| 179 | 179 | export class LinterInstaller extends BaseInstaller { | |
| 180 | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { | ||
| 180 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 181 | 181 | const isPylint = product === Product.pylint; | |
| 182 | 182 | ||
| 183 | 183 | const productName = ProductNames.get(product)!; | |
@@ -202,7 +202,7 @@ export class LinterInstaller extends BaseInstaller { | |||
| 202 | 202 | const response = await this.appShell.showErrorMessage(message, ...options); | |
| 203 | 203 | if (response === install) { | |
| 204 | 204 | sendTelemetryEvent(EventName.LINTER_NOT_INSTALLED_PROMPT, undefined, { tool: productName as LinterId, action: 'install' }); | |
| 205 | - return this.install(product, resource); | ||
| 205 | + return this.install(product, resource, cancel); | ||
| 206 | 206 | } else if (response === disableInstallPrompt) { | |
| 207 | 207 | await this.setStoredResponse(disableLinterInstallPromptKey, true); | |
| 208 | 208 | sendTelemetryEvent(EventName.LINTER_NOT_INSTALLED_PROMPT, undefined, { tool: productName as LinterId, action: 'disablePrompt' }); | |
@@ -250,7 +250,7 @@ export class LinterInstaller extends BaseInstaller { | |||
| 250 | 250 | } | |
| 251 | 251 | ||
| 252 | 252 | export class TestFrameworkInstaller extends BaseInstaller { | |
| 253 | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { | ||
| 253 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 254 | 254 | const productName = ProductNames.get(product)!; | |
| 255 | 255 | ||
| 256 | 256 | const options: string[] = []; | |
@@ -263,23 +263,23 @@ export class TestFrameworkInstaller extends BaseInstaller { | |||
| 263 | 263 | } | |
| 264 | 264 | ||
| 265 | 265 | const item = await this.appShell.showErrorMessage(message, ...options); | |
| 266 | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; | ||
| 266 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; | ||
| 267 | 267 | } | |
| 268 | 268 | } | |
| 269 | 269 | ||
| 270 | 270 | export class RefactoringLibraryInstaller extends BaseInstaller { | |
| 271 | - protected async promptToInstallImplementation(product: Product, resource?: Uri): Promise<InstallerResponse> { | ||
| 271 | + protected async promptToInstallImplementation(product: Product, resource?: Uri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 272 | 272 | const productName = ProductNames.get(product)!; | |
| 273 | 273 | const item = await this.appShell.showErrorMessage(`Refactoring library ${productName} is not installed. Install?`, 'Yes', 'No'); | |
| 274 | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; | ||
| 274 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; | ||
| 275 | 275 | } | |
| 276 | 276 | } | |
| 277 | 277 | ||
| 278 | 278 | export class DataScienceInstaller extends BaseInstaller { | |
| 279 | - protected async promptToInstallImplementation(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { | ||
| 279 | + protected async promptToInstallImplementation(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 280 | 280 | const productName = ProductNames.get(product)!; | |
| 281 | 281 | const item = await this.appShell.showErrorMessage(localize.DataScience.libraryNotInstalled().format(productName), 'Yes', 'No'); | |
| 282 | - return item === 'Yes' ? this.install(product, resource) : InstallerResponse.Ignore; | ||
| 282 | + return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; | ||
| 283 | 283 | } | |
| 284 | 284 | } | |
| 285 | 285 | ||
@@ -294,11 +294,11 @@ export class ProductInstaller implements IInstaller { | |||
| 294 | 294 | ||
| 295 | 295 | // tslint:disable-next-line:no-empty | |
| 296 | 296 | public dispose() { } | |
| 297 | - public async promptToInstall(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { | ||
| 298 | - return this.createInstaller(product).promptToInstall(product, resource); | ||
| 297 | + public async promptToInstall(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 298 | + return this.createInstaller(product).promptToInstall(product, resource, cancel); | ||
| 299 | 299 | } | |
| 300 | - public async install(product: Product, resource?: InterpreterUri): Promise<InstallerResponse> { | ||
| 301 | - return this.createInstaller(product).install(product, resource); | ||
| 300 | + public async install(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise<InstallerResponse> { | ||
| 301 | + return this.createInstaller(product).install(product, resource, cancel); | ||
| 302 | 302 | } | |
| 303 | 303 | public async isInstalled(product: Product, resource?: InterpreterUri): Promise<boolean | undefined> { | |
| 304 | 304 | return this.createInstaller(product).isInstalled(product, resource); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments