From 13a6727ec613ff0f748d832b4d239cf8adc63b58 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Wed, 31 Jan 2024 15:09:08 -0800 Subject: [PATCH 001/767] include multiple pytest versions in PR check (#22813) update PR check workflow to include testing Python tests against 3 versions of pytest: pre-release, stable release, and oldest supported version. --------- Co-authored-by: Karthik Nadig --- .github/workflows/pr-check.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 62dff5c876b3..5ec7027c74e5 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -94,7 +94,8 @@ jobs: # macOS runners are expensive, and we assume that Ubuntu is enough to cover the Unix case. os: [ubuntu-latest, windows-latest] # Run the tests on the oldest and most recent versions of Python. - python: ['3.8', '3.x', '3.12-dev'] + python: ['3.8', '3.x'] # run for 3 pytest versions, most recent stable, oldest version supported and pre-release + pytest-version: ['pytest', 'pytest@pre-release', 'pytest==6.2.0'] steps: - name: Checkout @@ -107,6 +108,18 @@ jobs: with: python-version: ${{ matrix.python }} + - name: Install specific pytest version + if: matrix.pytest-version == 'pytest@pre-release' + run: | + python -m pip install --pre pytest + + - name: Install specific pytest version + if: matrix.pytest-version != 'pytest@pre-release' + run: | + python -m pip install "${{ matrix.pytest-version }}" + + - name: Install specific pytest version + run: python -m pytest --version - name: Install base Python requirements uses: brettcannon/pip-secure-install@v1 with: From 1626c466e8f8b538378570cc974016dc498b114c Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 5 Feb 2024 16:26:56 +0530 Subject: [PATCH 002/767] Do not run commands to check whether shell integration is working (#22850) Closes https://github.com/microsoft/vscode-python/issues/22774 closes https://github.com/microsoft/vscode-python/issues/22743 --- .../shellIntegrationService.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index 485af6f3cc99..bfa963537ec9 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -5,6 +5,7 @@ import { injectable, inject } from 'inversify'; import { IApplicationShell, ITerminalManager, IWorkspaceService } from '../../common/application/types'; import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector'; import { TerminalShellType } from '../../common/terminal/types'; +import { IPersistentStateFactory } from '../../common/types'; import { createDeferred, sleep } from '../../common/utils/async'; import { cache } from '../../common/utils/decorators'; import { traceError, traceInfo, traceVerbose } from '../../logging'; @@ -22,12 +23,22 @@ const ShellIntegrationShells = [ TerminalShellType.fish, ]; +export const isShellIntegrationWorkingKey = 'SHELL_INTEGRATION_WORKING_KEY'; + @injectable() export class ShellIntegrationService implements IShellIntegrationService { + /** + * It seems to have a couple of issues: + * * Ends up cluterring terminal history + * * Does not work for hidden terminals: https://github.com/microsoft/vscode/issues/199611 + */ + private readonly USE_COMMAND_APPROACH = false; + constructor( @inject(ITerminalManager) private readonly terminalManager: ITerminalManager, @inject(IApplicationShell) private readonly appShell: IApplicationShell, @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, + @inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory, ) {} public async isWorking(shell: string): Promise { @@ -50,6 +61,23 @@ export class ShellIntegrationService implements IShellIntegrationService { if (!isSupposedToWork) { return false; } + if (!this.USE_COMMAND_APPROACH) { + // For now, based on problems with using the command approach, assume it always works. + return true; + } + const key = `${isShellIntegrationWorkingKey}_${shellType}`; + const persistedResult = this.persistentStateFactory.createGlobalPersistentState(key); + if (persistedResult.value !== undefined) { + return persistedResult.value; + } + const result = await this.checkIfWorkingByRunningCommand(shell); + // Persist result to storage to avoid running commands unncecessary. + await persistedResult.updateValue(result); + return result; + } + + private async checkIfWorkingByRunningCommand(shell: string): Promise { + const shellType = identifyShellFromShellPath(shell); const deferred = createDeferred(); const timestamp = new Date().getTime(); const name = `Python ${timestamp}`; From 8aaa70e4c54a2a3c2c350fb249d70af51a81a137 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 5 Feb 2024 16:27:10 +0530 Subject: [PATCH 003/767] Fix venv activation for cshell (#22852) Closes https://github.com/microsoft/vscode-python/issues/22822 Use current shell to figure out whether shell integration is working, even when using fallback shell for getting environment variables. --- src/client/terminals/envCollectionActivation/service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index b9a9fc17b3d5..4bce814576bb 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -188,7 +188,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ // PS1 in some cases is a shell variable (not an env variable) so "env" might not contain it, calculate it in that case. env.PS1 = await this.getPS1(shell, resource, env); - const prependOptions = await this.getPrependOptions(shell); + const prependOptions = await this.getPrependOptions(); // Clear any previously set env vars from collection envVarCollection.clear(); @@ -345,8 +345,8 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ } } - private async getPrependOptions(shell: string): Promise { - const isActive = await this.shellIntegrationService.isWorking(shell); + private async getPrependOptions(): Promise { + const isActive = await this.shellIntegrationService.isWorking(this.applicationEnvironment.shell); // Ideally we would want to prepend exactly once, either at shell integration or process creation. // TODO: Stop prepending altogether once https://github.com/microsoft/vscode/issues/145234 is available. return isActive From 20c1a10d05c3992b8fa20ce921ff863ccabb7926 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 5 Feb 2024 16:27:20 +0530 Subject: [PATCH 004/767] Log when running Python file (#22851) For https://github.com/microsoft/vscode-python/issues/22711 --- src/client/terminals/codeExecution/codeExecutionManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/terminals/codeExecution/codeExecutionManager.ts b/src/client/terminals/codeExecution/codeExecutionManager.ts index ed31e194b2d2..061918b7a74a 100644 --- a/src/client/terminals/codeExecution/codeExecutionManager.ts +++ b/src/client/terminals/codeExecution/codeExecutionManager.ts @@ -14,7 +14,7 @@ import { IDisposableRegistry, IConfigurationService, Resource } from '../../comm import { noop } from '../../common/utils/misc'; import { IInterpreterService } from '../../interpreter/contracts'; import { IServiceContainer } from '../../ioc/types'; -import { traceError } from '../../logging'; +import { traceError, traceVerbose } from '../../logging'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { ICodeExecutionHelper, ICodeExecutionManager, ICodeExecutionService } from '../../terminals/types'; @@ -44,6 +44,7 @@ export class CodeExecutionManager implements ICodeExecutionManager { (cmd) => { this.disposableRegistry.push( this.commandManager.registerCommand(cmd as any, async (file: Resource) => { + traceVerbose(`Attempting to run Python file`, file?.fsPath); const interpreterService = this.serviceContainer.get(IInterpreterService); const interpreter = await interpreterService.getActiveInterpreter(file); if (!interpreter) { From c0bf1b73b608d1c11b04f18966177335faea74b4 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 5 Feb 2024 20:40:46 +0530 Subject: [PATCH 005/767] Improve time taken to trigger language server startup once extension activation is triggered (#22514) For #22146 Improves time taken to trigger language server startup once extension activation is triggered - Do not block discovery on windows registry - Do not blocking auto-selection on validation of all interpreters - Make Windows Path locator faster --- src/client/activation/activationManager.ts | 9 +- src/client/activation/types.ts | 5 +- src/client/extension.ts | 3 +- src/client/extensionActivation.ts | 8 +- src/client/interpreter/autoSelection/index.ts | 47 ++++++--- src/client/interpreter/contracts.ts | 4 +- src/client/interpreter/interpreterService.ts | 10 +- src/client/languageServer/watcher.ts | 18 +++- src/client/pythonEnvironments/base/locator.ts | 2 +- .../pythonEnvironments/base/locatorUtils.ts | 4 +- .../locators/common/resourceBasedLocator.ts | 13 ++- .../composite/envsCollectionService.ts | 10 +- .../base/locators/composite/envsReducer.ts | 26 +++-- .../base/locators/composite/envsResolver.ts | 12 +-- .../lowLevel/windowsKnownPathsLocator.ts | 21 +++- .../lowLevel/windowsRegistryLocator.ts | 97 ++++++++++++++----- .../common/environmentManagers/conda.ts | 23 ++++- src/client/pythonEnvironments/legacyIOC.ts | 11 ++- src/client/telemetry/constants.ts | 1 + src/client/telemetry/index.ts | 9 ++ .../autoSelection/index.unit.test.ts | 27 +++++- src/test/pythonEnvironments/base/common.ts | 2 +- .../composite/envsReducer.unit.test.ts | 28 ------ .../windowsRegistryLocator.unit.test.ts | 6 +- ...indowsKnownPathsLocator.functional.test.ts | 15 +-- 25 files changed, 283 insertions(+), 128 deletions(-) diff --git a/src/client/activation/activationManager.ts b/src/client/activation/activationManager.ts index 763ce1ae8819..9e97c5c48857 100644 --- a/src/client/activation/activationManager.ts +++ b/src/client/activation/activationManager.ts @@ -11,6 +11,7 @@ import { PYTHON_LANGUAGE } from '../common/constants'; import { IFileSystem } from '../common/platform/types'; import { IDisposable, IInterpreterPathService, Resource } from '../common/types'; import { Deferred } from '../common/utils/async'; +import { StopWatch } from '../common/utils/stopWatch'; import { IInterpreterAutoSelectionService } from '../interpreter/autoSelection/types'; import { traceDecoratorError } from '../logging'; import { sendActivationTelemetry } from '../telemetry/envFileTelemetry'; @@ -69,7 +70,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager { } } - public async activate(): Promise { + public async activate(startupStopWatch: StopWatch): Promise { this.filterServices(); await this.initialize(); @@ -77,12 +78,12 @@ export class ExtensionActivationManager implements IExtensionActivationManager { await Promise.all([ ...this.singleActivationServices.map((item) => item.activate()), - this.activateWorkspace(this.activeResourceService.getActiveResource()), + this.activateWorkspace(this.activeResourceService.getActiveResource(), startupStopWatch), ]); } @traceDecoratorError('Failed to activate a workspace') - public async activateWorkspace(resource: Resource): Promise { + public async activateWorkspace(resource: Resource, startupStopWatch?: StopWatch): Promise { const folder = this.workspaceService.getWorkspaceFolder(resource); resource = folder ? folder.uri : undefined; const key = this.getWorkspaceKey(resource); @@ -97,7 +98,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager { await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource); } await sendActivationTelemetry(this.fileSystem, this.workspaceService, resource); - await Promise.all(this.activationServices.map((item) => item.activate(resource))); + await Promise.all(this.activationServices.map((item) => item.activate(resource, startupStopWatch))); await this.appDiagnostics.performPreStartupHealthCheck(resource); } diff --git a/src/client/activation/types.ts b/src/client/activation/types.ts index 2a177bb570b8..3a949e480d4b 100644 --- a/src/client/activation/types.ts +++ b/src/client/activation/types.ts @@ -6,6 +6,7 @@ import { Event } from 'vscode'; import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient/node'; import type { IDisposable, ILogOutputChannel, Resource } from '../common/types'; +import { StopWatch } from '../common/utils/stopWatch'; import { PythonEnvironment } from '../pythonEnvironments/info'; export const IExtensionActivationManager = Symbol('IExtensionActivationManager'); @@ -23,7 +24,7 @@ export interface IExtensionActivationManager extends IDisposable { * @returns {Promise} * @memberof IExtensionActivationManager */ - activate(): Promise; + activate(startupStopWatch: StopWatch): Promise; /** * Method invoked when a workspace is loaded. * This is where we place initialization scripts for each workspace. @@ -47,7 +48,7 @@ export const IExtensionActivationService = Symbol('IExtensionActivationService') */ export interface IExtensionActivationService { supportedWorkspaceTypes: { untrustedWorkspace: boolean; virtualWorkspace: boolean }; - activate(resource: Resource): Promise; + activate(resource: Resource, startupStopWatch?: StopWatch): Promise; } export enum LanguageServerType { diff --git a/src/client/extension.ts b/src/client/extension.ts index a4f5bd5dbfd0..a4c31d91eb3b 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -110,6 +110,7 @@ async function activateUnsafe( const activationDeferred = createDeferred(); displayProgress(activationDeferred.promise); startupDurations.startActivateTime = startupStopWatch.elapsedTime; + const activationStopWatch = new StopWatch(); //=============================================== // activation starts here @@ -127,7 +128,7 @@ async function activateUnsafe( const components = await initializeComponents(ext); // Then we finish activating. - const componentsActivated = await activateComponents(ext, components); + const componentsActivated = await activateComponents(ext, components, activationStopWatch); activateFeatures(ext, components); const nonBlocking = componentsActivated.map((r) => r.fullyReady); diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index cd9f99d400db..91497ea3ccdc 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -51,11 +51,13 @@ import { registerCreateEnvironmentTriggers } from './pythonEnvironments/creation import { initializePersistentStateForTriggers } from './common/persistentState'; import { logAndNotifyOnLegacySettings } from './logging/settingLogs'; import { DebuggerTypeName } from './debugger/constants'; +import { StopWatch } from './common/utils/stopWatch'; export async function activateComponents( // `ext` is passed to any extra activation funcs. ext: ExtensionState, components: Components, + startupStopWatch: StopWatch, ): Promise { // Note that each activation returns a promise that resolves // when that activation completes. However, it might have started @@ -73,7 +75,7 @@ export async function activateComponents( // activate them in parallel with the other components. // https://github.com/microsoft/vscode-python/issues/15380 // These will go away eventually once everything is refactored into components. - const legacyActivationResult = await activateLegacy(ext); + const legacyActivationResult = await activateLegacy(ext, startupStopWatch); const workspaceService = new WorkspaceService(); if (!workspaceService.isTrusted) { return [legacyActivationResult]; @@ -105,7 +107,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): // init and activation: move them to activateComponents(). // See https://github.com/microsoft/vscode-python/issues/10454. -async function activateLegacy(ext: ExtensionState): Promise { +async function activateLegacy(ext: ExtensionState, startupStopWatch: StopWatch): Promise { const { legacyIOC } = ext; const { serviceManager, serviceContainer } = legacyIOC; @@ -183,7 +185,7 @@ async function activateLegacy(ext: ExtensionState): Promise { const manager = serviceContainer.get(IExtensionActivationManager); disposables.push(manager); - const activationPromise = manager.activate(); + const activationPromise = manager.activate(startupStopWatch); return { fullyReady: activationPromise }; } diff --git a/src/client/interpreter/autoSelection/index.ts b/src/client/interpreter/autoSelection/index.ts index 7714c487ed30..4310374fc00f 100644 --- a/src/client/interpreter/autoSelection/index.ts +++ b/src/client/interpreter/autoSelection/index.ts @@ -6,11 +6,13 @@ import { inject, injectable } from 'inversify'; import { Event, EventEmitter, Uri } from 'vscode'; import { IWorkspaceService } from '../../common/application/types'; +import { DiscoveryUsingWorkers } from '../../common/experiments/groups'; import '../../common/extensions'; import { IFileSystem } from '../../common/platform/types'; -import { IPersistentState, IPersistentStateFactory, Resource } from '../../common/types'; +import { IExperimentService, IPersistentState, IPersistentStateFactory, Resource } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { compareSemVerLikeVersions } from '../../pythonEnvironments/base/info/pythonVersion'; +import { ProgressReportStage } from '../../pythonEnvironments/base/locator'; import { PythonEnvironment } from '../../pythonEnvironments/info'; import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; @@ -44,6 +46,7 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio @inject(IInterpreterComparer) private readonly envTypeComparer: IInterpreterComparer, @inject(IInterpreterAutoSelectionProxyService) proxy: IInterpreterAutoSelectionProxyService, @inject(IInterpreterHelper) private readonly interpreterHelper: IInterpreterHelper, + @inject(IExperimentService) private readonly experimentService: IExperimentService, ) { proxy.registerInstance!(this); } @@ -183,7 +186,7 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio private getAutoSelectionQueriedOnceState(): IPersistentState { const key = `autoSelectionInterpretersQueriedOnce`; - return this.stateFactory.createWorkspacePersistentState(key, undefined); + return this.stateFactory.createGlobalPersistentState(key, undefined); } /** @@ -199,22 +202,44 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio private async autoselectInterpreterWithLocators(resource: Resource): Promise { // Do not perform a full interpreter search if we already have cached interpreters for this workspace. const queriedState = this.getAutoSelectionInterpretersQueryState(resource); - if (queriedState.value !== true && resource) { + const globalQueriedState = this.getAutoSelectionQueriedOnceState(); + if (globalQueriedState.value && queriedState.value !== true && resource) { await this.interpreterService.triggerRefresh({ searchLocations: { roots: [resource], doNotIncludeNonRooted: true }, }); } - const globalQueriedState = this.getAutoSelectionQueriedOnceState(); - if (!globalQueriedState.value) { - // Global interpreters are loaded the first time an extension loads, after which we don't need to - // wait on global interpreter promise refresh. - await this.interpreterService.refreshPromise; - } - const interpreters = this.interpreterService.getInterpreters(resource); + const inExperiment = this.experimentService.inExperimentSync(DiscoveryUsingWorkers.experiment); const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource); + let recommendedInterpreter: PythonEnvironment | undefined; + if (inExperiment) { + if (!globalQueriedState.value) { + // Global interpreters are loaded the first time an extension loads, after which we don't need to + // wait on global interpreter promise refresh. + // Do not wait for validation of all interpreters to finish, we only need to validate the recommended interpreter. + await this.interpreterService.getRefreshPromise({ stage: ProgressReportStage.allPathsDiscovered }); + } + let interpreters = this.interpreterService.getInterpreters(resource); + + recommendedInterpreter = this.envTypeComparer.getRecommended(interpreters, workspaceUri?.folderUri); + const details = recommendedInterpreter + ? await this.interpreterService.getInterpreterDetails(recommendedInterpreter.path) + : undefined; + if (!details || !recommendedInterpreter) { + await this.interpreterService.refreshPromise; // Interpreter is invalid, wait for all of validation to finish. + interpreters = this.interpreterService.getInterpreters(resource); + recommendedInterpreter = this.envTypeComparer.getRecommended(interpreters, workspaceUri?.folderUri); + } + } else { + if (!globalQueriedState.value) { + // Global interpreters are loaded the first time an extension loads, after which we don't need to + // wait on global interpreter promise refresh. + await this.interpreterService.refreshPromise; + } + const interpreters = this.interpreterService.getInterpreters(resource); - const recommendedInterpreter = this.envTypeComparer.getRecommended(interpreters, workspaceUri?.folderUri); + recommendedInterpreter = this.envTypeComparer.getRecommended(interpreters, workspaceUri?.folderUri); + } if (!recommendedInterpreter) { return; } diff --git a/src/client/interpreter/contracts.ts b/src/client/interpreter/contracts.ts index bfaebd235f19..30a05c140249 100644 --- a/src/client/interpreter/contracts.ts +++ b/src/client/interpreter/contracts.ts @@ -4,6 +4,7 @@ import { FileChangeType } from '../common/platform/fileSystemWatcher'; import { Resource } from '../common/types'; import { PythonEnvSource } from '../pythonEnvironments/base/info'; import { + GetRefreshEnvironmentsOptions, ProgressNotificationEvent, PythonLocatorQuery, TriggerRefreshOptions, @@ -22,7 +23,7 @@ export const IComponentAdapter = Symbol('IComponentAdapter'); export interface IComponentAdapter { readonly onProgress: Event; triggerRefresh(query?: PythonLocatorQuery, options?: TriggerRefreshOptions): Promise; - getRefreshPromise(): Promise | undefined; + getRefreshPromise(options?: GetRefreshEnvironmentsOptions): Promise | undefined; readonly onChanged: Event; // VirtualEnvPrompt onDidCreate(resource: Resource, callback: () => void): Disposable; @@ -74,6 +75,7 @@ export const IInterpreterService = Symbol('IInterpreterService'); export interface IInterpreterService { triggerRefresh(query?: PythonLocatorQuery, options?: TriggerRefreshOptions): Promise; readonly refreshPromise: Promise | undefined; + getRefreshPromise(options?: GetRefreshEnvironmentsOptions): Promise | undefined; readonly onDidChangeInterpreters: Event; onDidChangeInterpreterConfiguration: Event; onDidChangeInterpreter: Event; diff --git a/src/client/interpreter/interpreterService.ts b/src/client/interpreter/interpreterService.ts index c97a35c4a973..e9829d978fb6 100644 --- a/src/client/interpreter/interpreterService.ts +++ b/src/client/interpreter/interpreterService.ts @@ -38,7 +38,11 @@ import { Interpreters } from '../common/utils/localize'; import { sendTelemetryEvent } from '../telemetry'; import { EventName } from '../telemetry/constants'; import { cache } from '../common/utils/decorators'; -import { PythonLocatorQuery, TriggerRefreshOptions } from '../pythonEnvironments/base/locator'; +import { + GetRefreshEnvironmentsOptions, + PythonLocatorQuery, + TriggerRefreshOptions, +} from '../pythonEnvironments/base/locator'; import { sleep } from '../common/utils/async'; type StoredPythonEnvironment = PythonEnvironment & { store?: boolean }; @@ -59,6 +63,10 @@ export class InterpreterService implements Disposable, IInterpreterService { return this.pyenvs.getRefreshPromise(); } + public getRefreshPromise(options?: GetRefreshEnvironmentsOptions): Promise | undefined { + return this.pyenvs.getRefreshPromise(options); + } + public get onDidChangeInterpreter(): Event { return this.didChangeInterpreterEmitter.event; } diff --git a/src/client/languageServer/watcher.ts b/src/client/languageServer/watcher.ts index d3eccb71144c..9f12dc89df0e 100644 --- a/src/client/languageServer/watcher.ts +++ b/src/client/languageServer/watcher.ts @@ -29,6 +29,7 @@ import { PylanceLSExtensionManager } from './pylanceLSExtensionManager'; import { ILanguageServerExtensionManager, ILanguageServerWatcher } from './types'; import { sendTelemetryEvent } from '../telemetry'; import { EventName } from '../telemetry/constants'; +import { StopWatch } from '../common/utils/stopWatch'; @injectable() /** @@ -73,14 +74,18 @@ export class LanguageServerWatcher implements IExtensionActivationService, ILang // IExtensionActivationService - public async activate(resource?: Resource): Promise { + public async activate(resource?: Resource, startupStopWatch?: StopWatch): Promise { this.register(); - await this.startLanguageServer(this.languageServerType, resource); + await this.startLanguageServer(this.languageServerType, resource, startupStopWatch); } // ILanguageServerWatcher - public async startLanguageServer(languageServerType: LanguageServerType, resource?: Resource): Promise { - await this.startAndGetLanguageServer(languageServerType, resource); + public async startLanguageServer( + languageServerType: LanguageServerType, + resource?: Resource, + startupStopWatch?: StopWatch, + ): Promise { + await this.startAndGetLanguageServer(languageServerType, resource, startupStopWatch); } public register(): void { @@ -124,6 +129,7 @@ export class LanguageServerWatcher implements IExtensionActivationService, ILang private async startAndGetLanguageServer( languageServerType: LanguageServerType, resource?: Resource, + startupStopWatch?: StopWatch, ): Promise { const lsResource = this.getWorkspaceUri(resource); const currentInterpreter = this.workspaceInterpreters.get(lsResource.fsPath); @@ -170,6 +176,10 @@ export class LanguageServerWatcher implements IExtensionActivationService, ILang if (languageServerExtensionManager.canStartLanguageServer(interpreter)) { // Start the language server. + if (startupStopWatch) { + // It means that startup is triggering this code, track time it takes since startup to activate this code. + sendTelemetryEvent(EventName.LANGUAGE_SERVER_TRIGGER_DURATION, startupStopWatch.elapsedTime); + } await languageServerExtensionManager.startLanguageServer(lsResource, interpreter); logStartup(languageServerType, lsResource); diff --git a/src/client/pythonEnvironments/base/locator.ts b/src/client/pythonEnvironments/base/locator.ts index ab3b17629bc5..3fd5194c37da 100644 --- a/src/client/pythonEnvironments/base/locator.ts +++ b/src/client/pythonEnvironments/base/locator.ts @@ -20,7 +20,7 @@ export type PythonEnvUpdatedEvent = { /** * The iteration index of The env info that was previously provided. */ - index: number; + index?: number; /** * The env info that was previously provided. */ diff --git a/src/client/pythonEnvironments/base/locatorUtils.ts b/src/client/pythonEnvironments/base/locatorUtils.ts index 97cb6298416f..faeaa84bedf4 100644 --- a/src/client/pythonEnvironments/base/locatorUtils.ts +++ b/src/client/pythonEnvironments/base/locatorUtils.ts @@ -85,7 +85,7 @@ export async function getEnvs(iterator: IPythonEnvsIterator(iterator: IPythonEnvsIterator imp await this.disposables.dispose(); } - public async *iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { - await this.activate(); + public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { const iterator = this.doIterEnvs(query); + const it = this._iterEnvs(iterator, query); + it.onUpdated = iterator.onUpdated; + return it; + } + + private async *_iterEnvs( + iterator: IPythonEnvsIterator, + query?: PythonLocatorQuery, + ): IPythonEnvsIterator { + await this.activate(); if (query?.envPath) { let result = await iterator.next(); while (!result.done) { diff --git a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts index cc37b1f82cfd..bcc9877ad14a 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts @@ -141,12 +141,12 @@ export class EnvsCollectionService extends PythonEnvsWatcher(); if (iterator.onUpdated !== undefined) { - const listener = iterator.onUpdated(async (event) => { + iterator.onUpdated(async (event) => { if (isProgressEvent(event)) { switch (event.stage) { case ProgressReportStage.discoveryFinished: state.done = true; - listener.dispose(); + // listener.dispose(); break; case ProgressReportStage.allPathsDiscovered: if (!query) { @@ -157,13 +157,17 @@ export class EnvsCollectionService extends PythonEnvsWatcher { - state.pending += 1; + iterator.onUpdated((event) => { if (isProgressEvent(event)) { if (event.stage === ProgressReportStage.discoveryFinished) { state.done = true; - listener.dispose(); + // For super slow locators such as Windows registry, we expect updates even after discovery + // is "officially" finished, hence do not dispose listeners. + // listener.dispose(); } else { didUpdate.fire(event); } @@ -63,19 +65,15 @@ async function* iterEnvsIterator( throw new Error( 'Unsupported behavior: `undefined` environment updates are not supported from downstream locators in reducer', ); - } else if (seen[event.index] !== undefined) { + } else if (event.index !== undefined && seen[event.index] !== undefined) { const oldEnv = seen[event.index]; seen[event.index] = event.update; didUpdate.fire({ index: event.index, old: oldEnv, update: event.update }); - } else { - // This implies a problem in a downstream locator - traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`); + } else if (event.update) { + didUpdate.fire({ update: event.update }); } - state.pending -= 1; checkIfFinishedAndNotify(state, didUpdate); }); - } else { - didUpdate.fire({ stage: ProgressReportStage.discoveryStarted }); } let result = await iterator.next(); @@ -91,10 +89,8 @@ async function* iterEnvsIterator( } result = await iterator.next(); } - if (iterator.onUpdated === undefined) { - state.done = true; - checkIfFinishedAndNotify(state, didUpdate); - } + state.done = true; + checkIfFinishedAndNotify(state, didUpdate); } async function resolveDifferencesInBackground( @@ -128,8 +124,8 @@ function checkIfFinishedAndNotify( ) { if (state.done && state.pending === 0) { didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); - didUpdate.dispose(); traceVerbose(`Finished with environment reducer`); + state.done = false; // No need to notify again. } } diff --git a/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts b/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts index 2ba54e07ed9c..752f5778c73c 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts @@ -81,13 +81,15 @@ export class PythonEnvsResolver implements IResolvingLocator { const seen: PythonEnvInfo[] = []; if (iterator.onUpdated !== undefined) { - const listener = iterator.onUpdated(async (event) => { + iterator.onUpdated(async (event) => { state.pending += 1; if (isProgressEvent(event)) { if (event.stage === ProgressReportStage.discoveryFinished) { didUpdate.fire({ stage: ProgressReportStage.allPathsDiscovered }); state.done = true; - listener.dispose(); + // For super slow locators such as Windows registry, we expect updates even after discovery + // is "officially" finished, hence do not dispose listeners. + // listener.dispose(); } else { didUpdate.fire(event); } @@ -95,15 +97,14 @@ export class PythonEnvsResolver implements IResolvingLocator { throw new Error( 'Unsupported behavior: `undefined` environment updates are not supported from downstream locators in resolver', ); - } else if (seen[event.index] !== undefined) { + } else if (event.index && seen[event.index] !== undefined) { const old = seen[event.index]; await setKind(event.update, environmentKinds); seen[event.index] = await resolveBasicEnv(event.update); didUpdate.fire({ old, index: event.index, update: seen[event.index] }); this.resolveInBackground(event.index, state, didUpdate, seen).ignoreErrors(); } else { - // This implies a problem in a downstream locator - traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`); + didUpdate.fire({ update: await this.resolveEnv(event.update.executablePath) }); } state.pending -= 1; checkIfFinishedAndNotify(state, didUpdate); @@ -173,7 +174,6 @@ function checkIfFinishedAndNotify( ) { if (state.done && state.pending === 0) { didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); - didUpdate.dispose(); traceVerbose(`Finished with environment resolver`); } } diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts index b2f5123069b0..d158d1da156c 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsKnownPathsLocator.ts @@ -4,10 +4,10 @@ /* eslint-disable max-classes-per-file */ import { Event } from 'vscode'; +import * as path from 'path'; import { IDisposable } from '../../../../common/types'; import { getSearchPathEntries } from '../../../../common/utils/exec'; import { Disposables } from '../../../../common/utils/resourceLifecycle'; -import { iterPythonExecutablesInDir, looksLikeBasicGlobalPython } from '../../../common/commonUtils'; import { isPyenvShimDir } from '../../../common/environmentManagers/pyenv'; import { isMicrosoftStoreDir } from '../../../common/environmentManagers/microsoftStoreEnv'; import { PythonEnvKind, PythonEnvSource } from '../../info'; @@ -17,6 +17,9 @@ import { getEnvs } from '../../locatorUtils'; import { PythonEnvsChangedEvent } from '../../watcher'; import { DirFilesLocator } from './filesLocator'; import { traceVerbose } from '../../../../logging'; +import { inExperiment, pathExists } from '../../../common/externalDependencies'; +import { DiscoveryUsingWorkers } from '../../../../common/experiments/groups'; +import { iterPythonExecutablesInDir, looksLikeBasicGlobalPython } from '../../../common/commonUtils'; /** * A locator for Windows locators found under the $PATH env var. @@ -34,6 +37,7 @@ export class WindowsPathEnvVarLocator implements ILocator, IDispos private readonly disposables = new Disposables(); constructor() { + const inExp = inExperiment(DiscoveryUsingWorkers.experiment); const dirLocators: (ILocator & IDisposable)[] = getSearchPathEntries() .filter( (dirname) => @@ -48,7 +52,7 @@ export class WindowsPathEnvVarLocator implements ILocator, IDispos !isMicrosoftStoreDir(dirname) && !isPyenvShimDir(dirname), ) // Build a locator for each directory. - .map((dirname) => getDirFilesLocator(dirname, PythonEnvKind.System, [PythonEnvSource.PathEnvVar])); + .map((dirname) => getDirFilesLocator(dirname, PythonEnvKind.System, [PythonEnvSource.PathEnvVar], inExp)); this.disposables.push(...dirLocators); this.locators = new Locators(dirLocators); this.onChanged = this.locators.onChanged; @@ -74,7 +78,7 @@ export class WindowsPathEnvVarLocator implements ILocator, IDispos } } -async function* getExecutables(dirname: string): AsyncIterableIterator { +async function* oldGetExecutables(dirname: string): AsyncIterableIterator { for await (const entry of iterPythonExecutablesInDir(dirname)) { if (await looksLikeBasicGlobalPython(entry)) { yield entry.filename; @@ -82,17 +86,26 @@ async function* getExecutables(dirname: string): AsyncIterableIterator { } } +async function* getExecutables(dirname: string): AsyncIterableIterator { + const executable = path.join(dirname, 'python.exe'); + if (await pathExists(executable)) { + yield executable; + } +} + function getDirFilesLocator( // These are passed through to DirFilesLocator. dirname: string, kind: PythonEnvKind, source?: PythonEnvSource[], + inExp?: boolean, ): ILocator & IDisposable { // For now we do not bother using a locator that watches for changes // in the directory. If we did then we would use // `DirFilesWatchingLocator`, but only if not \\windows\system32 and // the `isDirWatchable()` (from fsWatchingLocator.ts) returns true. - const locator = new DirFilesLocator(dirname, kind, getExecutables, source); + const executableFunc = inExp ? getExecutables : oldGetExecutables; + const locator = new DirFilesLocator(dirname, kind, executableFunc, source); const dispose = async () => undefined; // Really we should be checking for symlinks or something more diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts index 16b2167021db..c6ba64cdb469 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts @@ -1,9 +1,18 @@ +/* eslint-disable require-yield */ /* eslint-disable no-continue */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { EventEmitter } from 'vscode'; import { PythonEnvKind, PythonEnvSource } from '../../info'; -import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator'; +import { + BasicEnvInfo, + IPythonEnvsIterator, + Locator, + ProgressNotificationEvent, + ProgressReportStage, + PythonEnvUpdatedEvent, +} from '../../locator'; import { getRegistryInterpreters } from '../../../common/windowsUtils'; import { traceError, traceVerbose } from '../../../../logging'; import { isMicrosoftStoreDir } from '../../../common/environmentManagers/microsoftStoreEnv'; @@ -18,29 +27,69 @@ export class WindowsRegistryLocator extends Locator { _?: unknown, useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment), ): IPythonEnvsIterator { - const iterator = async function* () { - traceVerbose('Searching for windows registry interpreters'); - const interpreters = await getRegistryInterpreters(useWorkerThreads); - for (const interpreter of interpreters) { - try { - // Filter out Microsoft Store app directories. We have a store app locator that handles this. - // The python.exe available in these directories might not be python. It can be a store install - // shortcut that takes you to microsoft store. - if (isMicrosoftStoreDir(interpreter.interpreterPath)) { - continue; - } - const env: BasicEnvInfo = { - kind: PythonEnvKind.OtherGlobal, - executablePath: interpreter.interpreterPath, - source: [PythonEnvSource.WindowsRegistry], - }; - yield env; - } catch (ex) { - traceError(`Failed to process environment: ${interpreter}`, ex); - } + const didUpdate = new EventEmitter | ProgressNotificationEvent>(); + const iterator = useWorkerThreads ? iterEnvsIterator(didUpdate) : oldIterEnvsIterator(); + if (useWorkerThreads) { + iterator.onUpdated = didUpdate.event; + } + return iterator; + } +} + +/** + * Windows registry is slow and often not necessary, so notify completion immediately, while still updating lazily as we find stuff. + * To accomplish this, use an empty iterator while lazily firing environments using updates. + */ +async function* iterEnvsIterator( + didUpdate: EventEmitter | ProgressNotificationEvent>, +): IPythonEnvsIterator { + updateLazily(didUpdate).ignoreErrors(); +} + +async function updateLazily(didUpdate: EventEmitter | ProgressNotificationEvent>) { + traceVerbose('Searching for windows registry interpreters'); + const interpreters = await getRegistryInterpreters(true); + for (const interpreter of interpreters) { + try { + // Filter out Microsoft Store app directories. We have a store app locator that handles this. + // The python.exe available in these directories might not be python. It can be a store install + // shortcut that takes you to microsoft store. + if (isMicrosoftStoreDir(interpreter.interpreterPath)) { + continue; + } + const env: BasicEnvInfo = { + kind: PythonEnvKind.OtherGlobal, + executablePath: interpreter.interpreterPath, + source: [PythonEnvSource.WindowsRegistry], + }; + didUpdate.fire({ update: env }); + } catch (ex) { + traceError(`Failed to process environment: ${interpreter}`, ex); + } + } + didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); + traceVerbose('Finished searching for windows registry interpreters'); +} + +async function* oldIterEnvsIterator(): IPythonEnvsIterator { + const interpreters = await getRegistryInterpreters(false); + for (const interpreter of interpreters) { + try { + // Filter out Microsoft Store app directories. We have a store app locator that handles this. + // The python.exe available in these directories might not be python. It can be a store install + // shortcut that takes you to microsoft store. + if (isMicrosoftStoreDir(interpreter.interpreterPath)) { + continue; } - traceVerbose('Finished searching for windows registry interpreters'); - }; - return iterator(); + const env: BasicEnvInfo = { + kind: PythonEnvKind.OtherGlobal, + executablePath: interpreter.interpreterPath, + source: [PythonEnvSource.WindowsRegistry], + }; + yield env; + } catch (ex) { + traceError(`Failed to process environment: ${interpreter}`, ex); + } } + traceVerbose('Finished searching for windows registry interpreters'); } diff --git a/src/client/pythonEnvironments/common/environmentManagers/conda.ts b/src/client/pythonEnvironments/common/environmentManagers/conda.ts index bc98e57829e9..1cb2e490aef8 100644 --- a/src/client/pythonEnvironments/common/environmentManagers/conda.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/conda.ts @@ -10,6 +10,7 @@ import { readFile, onDidChangePythonSetting, exec, + inExperiment, } from '../externalDependencies'; import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../base/info'; @@ -24,6 +25,7 @@ import { OUTPUT_MARKER_SCRIPT } from '../../../common/process/internal/scripts'; import { splitLines } from '../../../common/stringUtils'; import { SpawnOptions } from '../../../common/process/types'; import { sleep } from '../../../common/utils/async'; +import { DiscoveryUsingWorkers } from '../../../common/experiments/groups'; export const AnacondaCompanyName = 'Anaconda, Inc.'; export const CONDAPATH_SETTING_KEY = 'condaPath'; @@ -269,8 +271,15 @@ export class Conda { readonly command: string, shellCommand?: string, private readonly shellPath?: string, - private readonly useWorkerThreads = true, + private readonly useWorkerThreads?: boolean, ) { + if (this.useWorkerThreads === undefined) { + try { + this.useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment); + } catch { + this.useWorkerThreads = false; // Temporarily support for legacy tests + } + } this.shellCommand = shellCommand ?? command; onDidChangePythonSetting(CONDAPATH_SETTING_KEY, () => { Conda.condaPromise = new Map>(); @@ -290,7 +299,15 @@ export class Conda { * * @return A Conda instance corresponding to the binary, if successful; otherwise, undefined. */ - private static async locate(shellPath?: string, useWorkerThreads = true): Promise { + private static async locate(shellPath?: string, useWorkerThread?: boolean): Promise { + let useWorkerThreads: boolean; + if (useWorkerThread === undefined) { + try { + useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment); + } catch { + useWorkerThreads = false; // Temporarily support for legacy tests + } + } traceVerbose(`Searching for conda.`); const home = getUserHomeDir(); let customCondaPath: string | undefined = 'conda'; @@ -395,7 +412,7 @@ export class Conda { // Probe the candidates, and pick the first one that exists and does what we need. for await (const condaPath of getCandidates()) { traceVerbose(`Probing conda binary: ${condaPath}`); - let conda = new Conda(condaPath, undefined, shellPath, useWorkerThreads); + let conda = new Conda(condaPath, undefined, shellPath); try { await conda.getInfo(); if (getOSType() === OSType.Windows && (isTestExecution() || condaPath !== customCondaPath)) { diff --git a/src/client/pythonEnvironments/legacyIOC.ts b/src/client/pythonEnvironments/legacyIOC.ts index f116bdb63a89..31de503a0f3c 100644 --- a/src/client/pythonEnvironments/legacyIOC.ts +++ b/src/client/pythonEnvironments/legacyIOC.ts @@ -9,7 +9,12 @@ import { Resource } from '../common/types'; import { IComponentAdapter, ICondaService, PythonEnvironmentsChangedEvent } from '../interpreter/contracts'; import { IServiceManager } from '../ioc/types'; import { PythonEnvInfo, PythonEnvKind, PythonEnvSource } from './base/info'; -import { IDiscoveryAPI, PythonLocatorQuery, TriggerRefreshOptions } from './base/locator'; +import { + GetRefreshEnvironmentsOptions, + IDiscoveryAPI, + PythonLocatorQuery, + TriggerRefreshOptions, +} from './base/locator'; import { isMacDefaultPythonPath } from './common/environmentManagers/macDefault'; import { isParentPath } from './common/externalDependencies'; import { EnvironmentType, PythonEnvironment } from './info'; @@ -102,8 +107,8 @@ class ComponentAdapter implements IComponentAdapter { return this.api.triggerRefresh(query, options); } - public getRefreshPromise() { - return this.api.getRefreshPromise(); + public getRefreshPromise(options?: GetRefreshEnvironmentsOptions) { + return this.api.getRefreshPromise(options); } public get onProgress() { diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index 072537619224..3321653c3bd7 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -64,6 +64,7 @@ export enum EventName { EXTENSION_SURVEY_PROMPT = 'EXTENSION_SURVEY_PROMPT', LANGUAGE_SERVER_ENABLED = 'LANGUAGE_SERVER.ENABLED', + LANGUAGE_SERVER_TRIGGER_DURATION = 'LANGUAGE_SERVER.TRIGGER_DURATION', LANGUAGE_SERVER_STARTUP = 'LANGUAGE_SERVER.STARTUP', LANGUAGE_SERVER_READY = 'LANGUAGE_SERVER.READY', LANGUAGE_SERVER_TELEMETRY = 'LANGUAGE_SERVER.EVENT', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 42a73fb06e07..50464414c2ed 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1325,6 +1325,15 @@ export interface IEventNamePropertyMapping { [EventName.LANGUAGE_SERVER_READY]: { lsVersion?: string; }; + /** + * Track how long it takes to trigger language server activation code, after Python extension starts activating. + */ + /* __GDPR__ + "language_server_trigger_duration" : { + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karrtikr" } + } + */ + [EventName.LANGUAGE_SERVER_TRIGGER_DURATION]: unknown; /** * Telemetry event sent when starting Node.js server */ diff --git a/src/test/interpreters/autoSelection/index.unit.test.ts b/src/test/interpreters/autoSelection/index.unit.test.ts index d96d590628e0..6c5473546614 100644 --- a/src/test/interpreters/autoSelection/index.unit.test.ts +++ b/src/test/interpreters/autoSelection/index.unit.test.ts @@ -14,7 +14,7 @@ import { WorkspaceService } from '../../../client/common/application/workspace'; import { PersistentState, PersistentStateFactory } from '../../../client/common/persistentState'; import { FileSystem } from '../../../client/common/platform/fileSystem'; import { IFileSystem } from '../../../client/common/platform/types'; -import { IPersistentStateFactory, Resource } from '../../../client/common/types'; +import { IExperimentService, IPersistentStateFactory, Resource } from '../../../client/common/types'; import { createDeferred } from '../../../client/common/utils/async'; import { InterpreterAutoSelectionService } from '../../../client/interpreter/autoSelection'; import { InterpreterAutoSelectionProxyService } from '../../../client/interpreter/autoSelection/proxy'; @@ -41,6 +41,7 @@ suite('Interpreters - Auto Selection', () => { let helper: IInterpreterHelper; let proxy: IInterpreterAutoSelectionProxyService; let interpreterService: IInterpreterService; + let experimentService: IExperimentService; let sendTelemetryEventStub: sinon.SinonStub; let telemetryEvents: { eventName: string; properties: Record }[] = []; class InterpreterAutoSelectionServiceTest extends InterpreterAutoSelectionService { @@ -64,6 +65,8 @@ suite('Interpreters - Auto Selection', () => { helper = mock(InterpreterHelper); proxy = mock(InterpreterAutoSelectionProxyService); interpreterService = mock(InterpreterService); + experimentService = mock(); + when(experimentService.inExperimentSync(anything())).thenReturn(false); const interpreterComparer = new EnvironmentTypeComparer(instance(helper)); @@ -75,6 +78,7 @@ suite('Interpreters - Auto Selection', () => { interpreterComparer, instance(proxy), instance(helper), + instance(experimentService), ); when(interpreterService.refreshPromise).thenReturn(undefined); @@ -140,6 +144,12 @@ suite('Interpreters - Auto Selection', () => { undefined, ), ).thenReturn(instance(state)); + when( + stateFactory.createGlobalPersistentState( + 'autoSelectionInterpretersQueriedOnce', + undefined, + ), + ).thenReturn(instance(state)); when(workspaceService.getWorkspaceFolderIdentifier(anything(), '')).thenReturn('workspaceIdentifier'); autoSelectionService.onDidChangeAutoSelectedInterpreter(() => { @@ -208,6 +218,13 @@ suite('Interpreters - Auto Selection', () => { test('getInterpreters is called with ignoreCache at true if there is no value set in the workspace persistent state', async () => { const interpreterComparer = new EnvironmentTypeComparer(instance(helper)); + + const globalQueriedState = mock(PersistentState) as PersistentState; + when(globalQueriedState.value).thenReturn(true); + when(stateFactory.createGlobalPersistentState(anyString(), undefined)).thenReturn( + instance(globalQueriedState), + ); + const queryState = mock(PersistentState) as PersistentState; when(queryState.value).thenReturn(undefined); @@ -236,6 +253,7 @@ suite('Interpreters - Auto Selection', () => { interpreterComparer, instance(proxy), instance(helper), + instance(experimentService), ); autoSelectionService.initializeStore = () => Promise.resolve(); @@ -275,6 +293,7 @@ suite('Interpreters - Auto Selection', () => { interpreterComparer, instance(proxy), instance(helper), + instance(experimentService), ); autoSelectionService.initializeStore = () => Promise.resolve(); @@ -309,6 +328,7 @@ suite('Interpreters - Auto Selection', () => { interpreterComparer, instance(proxy), instance(helper), + instance(experimentService), ); autoSelectionService.initializeStore = () => Promise.resolve(); @@ -352,6 +372,7 @@ suite('Interpreters - Auto Selection', () => { interpreterComparer, instance(proxy), instance(helper), + instance(experimentService), ); autoSelectionService.initializeStore = () => Promise.resolve(); @@ -385,6 +406,10 @@ suite('Interpreters - Auto Selection', () => { when(stateFactory.createWorkspacePersistentState(anyString(), undefined)).thenReturn( instance(queryState), ); + when(queryState.value).thenReturn(undefined); + when(stateFactory.createGlobalPersistentState(anyString(), undefined)).thenReturn( + instance(queryState), + ); let initialize = false; let eventFired = false; diff --git a/src/test/pythonEnvironments/base/common.ts b/src/test/pythonEnvironments/base/common.ts index 847d6e752273..9577e7ada490 100644 --- a/src/test/pythonEnvironments/base/common.ts +++ b/src/test/pythonEnvironments/base/common.ts @@ -203,7 +203,7 @@ export async function getEnvsWithUpdates( } updatesDone.resolve(); listener.dispose(); - } else { + } else if (event.index !== undefined) { const { index, update } = event; // We don't worry about if envs[index] is set already. envs[index] = update; diff --git a/src/test/pythonEnvironments/base/locators/composite/envsReducer.unit.test.ts b/src/test/pythonEnvironments/base/locators/composite/envsReducer.unit.test.ts index 592586118d14..a7f44abbbf94 100644 --- a/src/test/pythonEnvironments/base/locators/composite/envsReducer.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/composite/envsReducer.unit.test.ts @@ -3,16 +3,13 @@ import { assert, expect } from 'chai'; import * as path from 'path'; -import { EventEmitter } from 'vscode'; import { PythonEnvKind, PythonEnvSource } from '../../../../../client/pythonEnvironments/base/info'; import { PythonEnvsReducer } from '../../../../../client/pythonEnvironments/base/locators/composite/envsReducer'; import { PythonEnvsChangedEvent } from '../../../../../client/pythonEnvironments/base/watcher'; import { assertBasicEnvsEqual } from '../envTestUtils'; import { createBasicEnv, getEnvs, getEnvsWithUpdates, SimpleLocator } from '../../common'; import { - PythonEnvUpdatedEvent, BasicEnvInfo, - ProgressNotificationEvent, ProgressReportStage, isProgressEvent, } from '../../../../../client/pythonEnvironments/base/locator'; @@ -65,31 +62,6 @@ suite('Python envs locator - Environments Reducer', () => { assertBasicEnvsEqual(envs, expected); }); - test('Updates to environments from the incoming iterator replaces the previous info', async () => { - // Arrange - const env = createBasicEnv(PythonEnvKind.Poetry, path.join('path', 'to', 'exec1')); - const updatedEnv = createBasicEnv(PythonEnvKind.Venv, path.join('path', 'to', 'exec1')); - const envsReturnedByParentLocator = [env]; - const didUpdate = new EventEmitter | ProgressNotificationEvent>(); - const parentLocator = new SimpleLocator(envsReturnedByParentLocator, { - onUpdated: didUpdate.event, - }); - const reducer = new PythonEnvsReducer(parentLocator); - - // Act - const iterator = reducer.iterEnvs(); - - const iteratorUpdateCallback = () => { - didUpdate.fire({ index: 0, old: env, update: updatedEnv }); - didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); // It is essential for the incoming iterator to fire "null" event signifying it's done - }; - const envs = await getEnvsWithUpdates(iterator, iteratorUpdateCallback); - - // Assert - assertBasicEnvsEqual(envs, [updatedEnv]); - didUpdate.dispose(); - }); - test('Ensure progress updates are emitted correctly', async () => { // Arrange const env1 = createBasicEnv(PythonEnvKind.Venv, path.join('path', 'to', 'exec1')); diff --git a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts index a69a643f52d4..4197c36fa9f7 100644 --- a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts @@ -222,7 +222,7 @@ suite('Windows Registry', () => { createBasicEnv(PythonEnvKind.OtherGlobal, path.join(regTestRoot, 'python38', 'python.exe')), ].map((e) => ({ ...e, source: [PythonEnvSource.WindowsRegistry] })); - const iterator = locator.iterEnvs(); + const iterator = locator.iterEnvs(undefined, true); const actualEnvs = await getEnvs(iterator); assertBasicEnvsEqual(actualEnvs, expectedEnvs); @@ -233,7 +233,7 @@ suite('Windows Registry', () => { throw Error(); }); - const iterator = locator.iterEnvs(); + const iterator = locator.iterEnvs(undefined, true); const actualEnvs = await getEnvs(iterator); assert.deepStrictEqual(actualEnvs, []); @@ -252,7 +252,7 @@ suite('Windows Registry', () => { createBasicEnv(PythonEnvKind.OtherGlobal, path.join(regTestRoot, 'python38', 'python.exe')), ].map((e) => ({ ...e, source: [PythonEnvSource.WindowsRegistry] })); - const iterator = locator.iterEnvs(); + const iterator = locator.iterEnvs(undefined, true); const actualEnvs = await getEnvs(iterator); assertBasicEnvsEqual(actualEnvs, expectedEnvs); diff --git a/src/test/pythonEnvironments/discovery/locators/windowsKnownPathsLocator.functional.test.ts b/src/test/pythonEnvironments/discovery/locators/windowsKnownPathsLocator.functional.test.ts index cd7715a1bf75..ebebf2a8220e 100644 --- a/src/test/pythonEnvironments/discovery/locators/windowsKnownPathsLocator.functional.test.ts +++ b/src/test/pythonEnvironments/discovery/locators/windowsKnownPathsLocator.functional.test.ts @@ -3,6 +3,7 @@ import { assert } from 'chai'; import * as path from 'path'; +import * as sinon from 'sinon'; import { getOSType, OSType } from '../../../../client/common/utils/platform'; import { PythonEnvKind, PythonEnvSource } from '../../../../client/pythonEnvironments/base/info'; import { BasicEnvInfo, PythonLocatorQuery } from '../../../../client/pythonEnvironments/base/locator'; @@ -10,6 +11,7 @@ import { WindowsPathEnvVarLocator } from '../../../../client/pythonEnvironments/ import { ensureFSTree } from '../../../utils/fs'; import { assertBasicEnvsEqual } from '../../base/locators/envTestUtils'; import { createBasicEnv, getEnvs } from '../../base/common'; +import * as externalDependencies from '../../../../client/pythonEnvironments/common/externalDependencies'; const IS_WINDOWS = getOSType() === OSType.Windows; @@ -71,17 +73,17 @@ suite('Python envs locator - WindowsPathEnvVarLocator', async () => { if (!process.env.PVSC_TEST_FORCE) { this.skip(); } - // eslint-disable-next-line global-require - const sinon = require('sinon'); + } + await ensureFSTree(dataTree, __dirname); + }); + setup(async () => { + if (!IS_WINDOWS) { // eslint-disable-next-line global-require const platformAPI = require('../../../../../client/common/utils/platform'); const stub = sinon.stub(platformAPI, 'getOSType'); stub.returns(OSType.Windows); } - - await ensureFSTree(dataTree, __dirname); - }); - setup(() => { + sinon.stub(externalDependencies, 'inExperiment').returns(true); cleanUps = []; const oldSearchPath = process.env[ENV_VAR]; @@ -97,6 +99,7 @@ suite('Python envs locator - WindowsPathEnvVarLocator', async () => { console.log(err); } }); + sinon.restore(); }); function getActiveLocator(...roots: string[]): WindowsPathEnvVarLocator { From d674a170a93495c55df5402705b3fcd8a029cd58 Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Tue, 6 Feb 2024 18:28:19 -0800 Subject: [PATCH 006/767] Fix Run Recent Command Caching Issue (#22867) Resolves: #22811 Fixing caching issue where users were experiencing cached success/failure decoration that were impacted when using `Terminal: Run Recent Command` on success/failure commands --- pythonFiles/pythonrc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/pythonrc.py b/pythonFiles/pythonrc.py index 1cb72b0ec344..374888ddada5 100644 --- a/pythonFiles/pythonrc.py +++ b/pythonFiles/pythonrc.py @@ -49,7 +49,7 @@ def __str__(self): exit_code = 1 else: exit_code = 0 - + self.hooks.failure_flag = False # Guide following official VS Code doc for shell integration sequence: result = "" # For non-windows allow recent_command history. From 403071707c559feda67ae7ce808547b14ce1f92e Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Wed, 7 Feb 2024 22:31:52 +0530 Subject: [PATCH 007/767] Use terminal data write event to figure out whether shell integration is working (#22872) Closes https://github.com/microsoft/vscode-python/issues/22439 Blocked on https://github.com/microsoft/vscode/issues/204616 --- .../shellIntegrationService.ts | 81 +++++++++++++++++-- src/client/terminals/types.ts | 1 + 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index bfa963537ec9..bb5168bc6e09 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -2,10 +2,16 @@ // Licensed under the MIT License. import { injectable, inject } from 'inversify'; -import { IApplicationShell, ITerminalManager, IWorkspaceService } from '../../common/application/types'; +import { EventEmitter } from 'vscode'; +import { + IApplicationEnvironment, + IApplicationShell, + ITerminalManager, + IWorkspaceService, +} from '../../common/application/types'; import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector'; import { TerminalShellType } from '../../common/terminal/types'; -import { IPersistentStateFactory } from '../../common/types'; +import { IDisposableRegistry, IPersistentStateFactory } from '../../common/types'; import { createDeferred, sleep } from '../../common/utils/async'; import { cache } from '../../common/utils/decorators'; import { traceError, traceInfo, traceVerbose } from '../../logging'; @@ -34,12 +40,55 @@ export class ShellIntegrationService implements IShellIntegrationService { */ private readonly USE_COMMAND_APPROACH = false; + private isWorkingForShell = new Set(); + + private readonly didChange = new EventEmitter(); + + private isDataWriteEventWorking = true; + constructor( @inject(ITerminalManager) private readonly terminalManager: ITerminalManager, @inject(IApplicationShell) private readonly appShell: IApplicationShell, @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, @inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory, - ) {} + @inject(IApplicationEnvironment) private readonly appEnvironment: IApplicationEnvironment, + @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, + ) { + try { + this.appShell.onDidWriteTerminalData( + (e) => { + if (e.data.includes('\x1b]633;A\x07')) { + let { shell } = this.appEnvironment; + if ('shellPath' in e.terminal.creationOptions && e.terminal.creationOptions.shellPath) { + shell = e.terminal.creationOptions.shellPath; + } + const shellType = identifyShellFromShellPath(shell); + const wasWorking = this.isWorkingForShell.has(shellType); + this.isWorkingForShell.add(shellType); + if (!wasWorking) { + // If it wasn't working previously, status has changed. + this.didChange.fire(); + } + } + }, + this, + this.disposables, + ); + this.appEnvironment.onDidChangeShell( + async (shell: string) => { + this.createDummyHiddenTerminal(shell); + }, + this, + this.disposables, + ); + this.createDummyHiddenTerminal(this.appEnvironment.shell); + } catch (ex) { + this.isDataWriteEventWorking = false; + traceError('Unable to check if shell integration is active', ex); + } + } + + public readonly onDidChangeStatus = this.didChange.event; public async isWorking(shell: string): Promise { return this._isWorking(shell).catch((ex) => { @@ -62,8 +111,20 @@ export class ShellIntegrationService implements IShellIntegrationService { return false; } if (!this.USE_COMMAND_APPROACH) { - // For now, based on problems with using the command approach, assume it always works. - return true; + // For now, based on problems with using the command approach, use terminal data write event. + if (!this.isDataWriteEventWorking) { + // Assume shell integration is working, if data write event isn't working. + return true; + } + if (shellType === TerminalShellType.powershell || shellType === TerminalShellType.powershellCore) { + // Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now. + return true; + } + if (!this.isWorkingForShell.has(shellType)) { + // Maybe data write event has not been processed yet, wait a bit. + await sleep(1000); + } + return this.isWorkingForShell.has(shellType); } const key = `${isShellIntegrationWorkingKey}_${shellType}`; const persistedResult = this.persistentStateFactory.createGlobalPersistentState(key); @@ -76,6 +137,16 @@ export class ShellIntegrationService implements IShellIntegrationService { return result; } + /** + * Creates a dummy terminal so that we are guaranteed a data write event for this shell type. + */ + private createDummyHiddenTerminal(shell: string) { + this.terminalManager.createTerminal({ + shellPath: shell, + hideFromUser: true, + }); + } + private async checkIfWorkingByRunningCommand(shell: string): Promise { const shellType = identifyShellFromShellPath(shell); const deferred = createDeferred(); diff --git a/src/client/terminals/types.ts b/src/client/terminals/types.ts index 0fb268fe192c..fbdd490c175c 100644 --- a/src/client/terminals/types.ts +++ b/src/client/terminals/types.ts @@ -44,6 +44,7 @@ export interface ITerminalEnvVarCollectionService { export const IShellIntegrationService = Symbol('IShellIntegrationService'); export interface IShellIntegrationService { + onDidChangeStatus: Event; isWorking(shell: string): Promise; } From c4c05a6c570f9273ee1d12e89701ac625fb6bac1 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Thu, 8 Feb 2024 11:49:44 -0800 Subject: [PATCH 008/767] update tree comparison for tests to be order independent for children (#22832) following the introduction of pytest 8, the order in which children were listed changed. Since the order is not important, this updates the tests to make the tests not consider order of children when comparing actual and expected outcomes of test runs. --- .../test_bottom_folder.py | 0 .../expected_discovery_test_output.py | 22 +++++++++---------- .../expected_execution_test_output.py | 14 ++++++------ .../tests/pytestadapter/test_discovery.py | 21 ++++++++++++------ .../tests/pytestadapter/test_execution.py | 4 ++-- .../helpers.py => tree_comparison_helper.py} | 9 +++----- .../expected_discovery_test_output.py | 4 +++- .../tests/unittestadapter/test_discovery.py | 9 +++++++- .../tests/unittestadapter/test_utils.py | 9 +++++++- 9 files changed, 56 insertions(+), 36 deletions(-) rename pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/{z_nested_folder_one => nested_folder_one}/test_bottom_folder.py (100%) rename pythonFiles/tests/{unittestadapter/helpers.py => tree_comparison_helper.py} (80%) diff --git a/pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py b/pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py rename to pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index d4e91f56b5fe..2d86710e776b 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -317,7 +317,7 @@ # └── test_top_folder.py # └── test_top_function_t # └── test_top_function_f -# └── z_nested_folder_one +# └── nested_folder_one # └── test_bottom_folder.py # └── test_bottom_function_t # └── test_bottom_function_f @@ -326,14 +326,14 @@ TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" ) -test_z_nested_folder_one_path = ( - TEST_DATA_PATH / "dual_level_nested_folder" / "z_nested_folder_one" +test_nested_folder_one_path = ( + TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" ) test_bottom_folder_path = ( TEST_DATA_PATH / "dual_level_nested_folder" - / "z_nested_folder_one" + / "nested_folder_one" / "test_bottom_folder.py" ) @@ -392,10 +392,10 @@ ], }, { - "name": "z_nested_folder_one", - "path": os.fspath(test_z_nested_folder_one_path), + "name": "nested_folder_one", + "path": os.fspath(test_nested_folder_one_path), "type_": "folder", - "id_": os.fspath(test_z_nested_folder_one_path), + "id_": os.fspath(test_nested_folder_one_path), "children": [ { "name": "test_bottom_folder.py", @@ -412,11 +412,11 @@ ), "type_": "test", "id_": get_absolute_test_id( - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", test_bottom_folder_path, ), "runID": get_absolute_test_id( - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", test_bottom_folder_path, ), }, @@ -429,11 +429,11 @@ ), "type_": "test", "id_": get_absolute_test_id( - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", test_bottom_folder_path, ), "runID": get_absolute_test_id( - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", test_bottom_folder_path, ), }, diff --git a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py index cf8997a252d3..44f3d3d0abce 100644 --- a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py @@ -308,7 +308,7 @@ # └── test_top_folder.py # └── test_top_function_t: success # └── test_top_function_f: failure -# └── z_nested_folder_one +# └── nested_folder_one # └── test_bottom_folder.py # └── test_bottom_function_t: success # └── test_bottom_function_f: failure @@ -318,7 +318,7 @@ dual_level_nested_folder_bottom_path = ( TEST_DATA_PATH / "dual_level_nested_folder" - / "z_nested_folder_one" + / "nested_folder_one" / "test_bottom_folder.py" ) dual_level_nested_folder_execution_expected_output = { @@ -345,11 +345,11 @@ "subtest": None, }, get_absolute_test_id( - "z_nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "nested_folder_one/test_bottom_folder.py::test_bottom_function_t", dual_level_nested_folder_bottom_path, ): { "test": get_absolute_test_id( - "z_nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "nested_folder_one/test_bottom_folder.py::test_bottom_function_t", dual_level_nested_folder_bottom_path, ), "outcome": "success", @@ -358,11 +358,11 @@ "subtest": None, }, get_absolute_test_id( - "z_nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "nested_folder_one/test_bottom_folder.py::test_bottom_function_f", dual_level_nested_folder_bottom_path, ): { "test": get_absolute_test_id( - "z_nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "nested_folder_one/test_bottom_folder.py::test_bottom_function_f", dual_level_nested_folder_bottom_path, ), "outcome": "failure", @@ -479,7 +479,7 @@ dual_level_nested_folder_bottom_path = ( TEST_DATA_PATH / "dual_level_nested_folder" - / "z_nested_folder_one" + / "nested_folder_one" / "test_bottom_folder.py" ) unittest_folder_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 2630ddef68b0..9956b82a6345 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -1,11 +1,18 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import os +import pathlib import shutil +import sys from typing import Any, Dict, List, Optional import pytest +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) + +from tests.tree_comparison_helper import is_same_tree + from . import expected_discovery_test_output from .helpers import TEST_DATA_PATH, runner, runner_with_cwd @@ -195,7 +202,7 @@ def test_pytest_collect(file, expected_const): assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) - assert actual_item.get("tests") == expected_const + assert is_same_tree(actual_item.get("tests"), expected_const) def test_pytest_root_dir(): @@ -219,9 +226,9 @@ def test_pytest_root_dir(): assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH / "root") - assert ( - actual_item.get("tests") - == expected_discovery_test_output.root_with_config_expected_output + assert is_same_tree( + actual_item.get("tests"), + expected_discovery_test_output.root_with_config_expected_output, ) @@ -245,7 +252,7 @@ def test_pytest_config_file(): assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH / "root") - assert ( - actual_item.get("tests") - == expected_discovery_test_output.root_with_config_expected_output + assert is_same_tree( + actual_item.get("tests"), + expected_discovery_test_output.root_with_config_expected_output, ) diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 767d54a6cabe..dd32b61fa262 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -193,8 +193,8 @@ def test_bad_id_error_execution(): [ "dual_level_nested_folder/test_top_folder.py::test_top_function_t", "dual_level_nested_folder/test_top_folder.py::test_top_function_f", - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_t", - "dual_level_nested_folder/z_nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", ], expected_execution_test_output.dual_level_nested_folder_execution_expected_output, ), diff --git a/pythonFiles/tests/unittestadapter/helpers.py b/pythonFiles/tests/tree_comparison_helper.py similarity index 80% rename from pythonFiles/tests/unittestadapter/helpers.py rename to pythonFiles/tests/tree_comparison_helper.py index 303d021368f7..edf6aa8ff869 100644 --- a/pythonFiles/tests/unittestadapter/helpers.py +++ b/pythonFiles/tests/tree_comparison_helper.py @@ -1,10 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import pathlib - -TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" - def is_same_tree(tree1, tree2) -> bool: """Helper function to test if two test trees are the same. @@ -17,8 +13,9 @@ def is_same_tree(tree1, tree2) -> bool: # Compare child test nodes if they exist, otherwise compare test items. if "children" in tree1 and "children" in tree2: - children1 = tree1["children"] - children2 = tree2["children"] + # sort children by path before comparing since order doesn't matter of children + children1 = sorted(tree1["children"], key=lambda x: x["path"]) + children2 = sorted(tree2["children"], key=lambda x: x["path"]) # Compare test nodes. if len(children1) != len(children2): diff --git a/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py b/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py index db509ebeca3c..1007a8f42dfd 100644 --- a/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py @@ -3,9 +3,11 @@ import os from unittestadapter.pvsc_utils import TestNodeTypeEnum -from .helpers import TEST_DATA_PATH import pathlib +TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" + + skip_unittest_folder_discovery_output = { "path": os.fspath(TEST_DATA_PATH / "unittest_skip"), "name": "unittest_skip", diff --git a/pythonFiles/tests/unittestadapter/test_discovery.py b/pythonFiles/tests/unittestadapter/test_discovery.py index 4249ca4faef2..a68774d3f2dc 100644 --- a/pythonFiles/tests/unittestadapter/test_discovery.py +++ b/pythonFiles/tests/unittestadapter/test_discovery.py @@ -3,14 +3,21 @@ import os import pathlib +import sys from typing import List import pytest from unittestadapter.discovery import discover_tests from unittestadapter.pvsc_utils import TestNodeTypeEnum, parse_unittest_args +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) + + from . import expected_discovery_test_output -from .helpers import TEST_DATA_PATH, is_same_tree +from tests.tree_comparison_helper import is_same_tree + +TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" @pytest.mark.parametrize( diff --git a/pythonFiles/tests/unittestadapter/test_utils.py b/pythonFiles/tests/unittestadapter/test_utils.py index 9fe2af8256cd..d5f6fbbe9f18 100644 --- a/pythonFiles/tests/unittestadapter/test_utils.py +++ b/pythonFiles/tests/unittestadapter/test_utils.py @@ -3,6 +3,7 @@ import os import pathlib +import sys import unittest import pytest @@ -15,7 +16,13 @@ get_test_case, ) -from .helpers import TEST_DATA_PATH, is_same_tree +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) + +from tests.tree_comparison_helper import is_same_tree + + +TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" @pytest.mark.parametrize( From 8496dfec41f8df9b9496067efa31583ab6420c7b Mon Sep 17 00:00:00 2001 From: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:18:12 -0600 Subject: [PATCH 009/767] Remove experimental flag for create env prompt (#22892) Remove experimental flag for `python.createEnvironment.trigger`. --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 4914c57cf96e..c211df36a0d6 100644 --- a/package.json +++ b/package.json @@ -483,9 +483,6 @@ "enum": [ "off", "prompt" - ], - "tags": [ - "experimental" ] }, "python.condaPath": { From b0c34e32d29e002d7bff673c82072edd0c570619 Mon Sep 17 00:00:00 2001 From: Aydar Kamaltdinov Date: Fri, 9 Feb 2024 23:25:46 +0500 Subject: [PATCH 010/767] Add UnicodeDecodeError catching (#22873) Resolve `UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in position 48995: unexpected end of data` Co-authored-by: Aydar Kamaltdinov --- pythonFiles/vscode_pytest/run_pytest_script.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonFiles/vscode_pytest/run_pytest_script.py b/pythonFiles/vscode_pytest/run_pytest_script.py index 8005f3a32c0e..d6381e9b520b 100644 --- a/pythonFiles/vscode_pytest/run_pytest_script.py +++ b/pythonFiles/vscode_pytest/run_pytest_script.py @@ -58,6 +58,8 @@ except json.JSONDecodeError: # JSON decoding error, the complete JSON object is not yet received continue + except UnicodeDecodeError: + continue except socket.error as e: print(f"Error: Could not connect to runTestIdsPort: {e}") print("Error: Could not connect to runTestIdsPort") From 5174d5c1d552cdf0fa57b1318a6ea5d04d7917bb Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Sun, 11 Feb 2024 00:08:44 +0530 Subject: [PATCH 011/767] Improve shell integration reliability for zsh (#22891) Closes https://github.com/microsoft/vscode-python/issues/22881 If status changes, re-run activation. Also persist once we know shell integration works for a shell. --- .../envCollectionActivation/service.ts | 14 +- .../shellIntegrationService.ts | 124 +++++++----------- 2 files changed, 63 insertions(+), 75 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index 4bce814576bb..a450c5b02162 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -26,7 +26,7 @@ import { IPathUtils, } from '../../common/types'; import { Interpreters } from '../../common/utils/localize'; -import { traceError, traceVerbose, traceWarn } from '../../logging'; +import { traceError, traceInfo, traceVerbose, traceWarn } from '../../logging'; import { IInterpreterService } from '../../interpreter/contracts'; import { defaultShells } from '../../interpreter/activation/service'; import { IEnvironmentActivationService } from '../../interpreter/activation/types'; @@ -111,6 +111,14 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ this, this.disposables, ); + this.shellIntegrationService.onDidChangeStatus( + async () => { + traceInfo("Shell integration status changed, can confirm it's working."); + await this._applyCollection(undefined).ignoreErrors(); + }, + this, + this.disposables, + ); this.applicationEnvironment.onDidChangeShell( async (shell: string) => { this.processEnvVars = undefined; @@ -125,7 +133,9 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ const isActive = await this.shellIntegrationService.isWorking(shell); const shellType = identifyShellFromShellPath(shell); if (!isActive && shellType !== TerminalShellType.commandPrompt) { - traceWarn(`Shell integration is not active, environment activated maybe overriden by the shell.`); + traceWarn( + `Shell integration may not be active, environment activated may be overridden by the shell.`, + ); } this.registeredOnce = true; } diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index bb5168bc6e09..9846b0c36f23 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -12,9 +12,8 @@ import { import { identifyShellFromShellPath } from '../../common/terminal/shellDetectors/baseShellDetector'; import { TerminalShellType } from '../../common/terminal/types'; import { IDisposableRegistry, IPersistentStateFactory } from '../../common/types'; -import { createDeferred, sleep } from '../../common/utils/async'; -import { cache } from '../../common/utils/decorators'; -import { traceError, traceInfo, traceVerbose } from '../../logging'; +import { sleep } from '../../common/utils/async'; +import { traceError, traceVerbose } from '../../logging'; import { IShellIntegrationService } from '../types'; /** @@ -29,17 +28,12 @@ const ShellIntegrationShells = [ TerminalShellType.fish, ]; -export const isShellIntegrationWorkingKey = 'SHELL_INTEGRATION_WORKING_KEY'; +export enum isShellIntegrationWorking { + key = 'SHELL_INTEGRATION_WORKING_KEY', +} @injectable() export class ShellIntegrationService implements IShellIntegrationService { - /** - * It seems to have a couple of issues: - * * Ends up cluterring terminal history - * * Does not work for hidden terminals: https://github.com/microsoft/vscode/issues/199611 - */ - private readonly USE_COMMAND_APPROACH = false; - private isWorkingForShell = new Set(); private readonly didChange = new EventEmitter(); @@ -55,6 +49,12 @@ export class ShellIntegrationService implements IShellIntegrationService { @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, ) { try { + const activeShellType = identifyShellFromShellPath(this.appEnvironment.shell); + const key = getKeyForShell(activeShellType); + const persistedResult = this.persistentStateFactory.createGlobalPersistentState(key); + if (persistedResult.value) { + this.isWorkingForShell.add(activeShellType); + } this.appShell.onDidWriteTerminalData( (e) => { if (e.data.includes('\x1b]633;A\x07')) { @@ -63,6 +63,7 @@ export class ShellIntegrationService implements IShellIntegrationService { shell = e.terminal.creationOptions.shellPath; } const shellType = identifyShellFromShellPath(shell); + traceVerbose('Received shell integration sequence for', shellType); const wasWorking = this.isWorkingForShell.has(shellType); this.isWorkingForShell.add(shellType); if (!wasWorking) { @@ -86,6 +87,12 @@ export class ShellIntegrationService implements IShellIntegrationService { this.isDataWriteEventWorking = false; traceError('Unable to check if shell integration is active', ex); } + const isEnabled = !!this.workspaceService + .getConfiguration('terminal') + .get('integrated.shellIntegration.enabled'); + if (!isEnabled) { + traceVerbose('Shell integration is disabled in user settings.'); + } } public readonly onDidChangeStatus = this.didChange.event; @@ -97,46 +104,48 @@ export class ShellIntegrationService implements IShellIntegrationService { }); } - @cache(-1, true) public async _isWorking(shell: string): Promise { - const isEnabled = this.workspaceService - .getConfiguration('terminal') - .get('integrated.shellIntegration.enabled')!; - if (!isEnabled) { - traceVerbose('Shell integrated is disabled in user settings.'); - } const shellType = identifyShellFromShellPath(shell); - const isSupposedToWork = isEnabled && ShellIntegrationShells.includes(shellType); + const isSupposedToWork = ShellIntegrationShells.includes(shellType); if (!isSupposedToWork) { return false; } - if (!this.USE_COMMAND_APPROACH) { - // For now, based on problems with using the command approach, use terminal data write event. - if (!this.isDataWriteEventWorking) { - // Assume shell integration is working, if data write event isn't working. - return true; - } - if (shellType === TerminalShellType.powershell || shellType === TerminalShellType.powershellCore) { - // Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now. - return true; - } - if (!this.isWorkingForShell.has(shellType)) { - // Maybe data write event has not been processed yet, wait a bit. - await sleep(1000); - } - return this.isWorkingForShell.has(shellType); - } - const key = `${isShellIntegrationWorkingKey}_${shellType}`; + const key = getKeyForShell(shellType); const persistedResult = this.persistentStateFactory.createGlobalPersistentState(key); if (persistedResult.value !== undefined) { return persistedResult.value; } - const result = await this.checkIfWorkingByRunningCommand(shell); - // Persist result to storage to avoid running commands unncecessary. - await persistedResult.updateValue(result); + const result = await this.useDataWriteApproach(shellType); + if (result) { + // Once we know that shell integration is working for a shell, persist it so we need not do this check every session. + await persistedResult.updateValue(result); + } return result; } + private async useDataWriteApproach(shellType: TerminalShellType) { + // For now, based on problems with using the command approach, use terminal data write event. + if (!this.isDataWriteEventWorking) { + // Assume shell integration is working, if data write event isn't working. + return true; + } + if (shellType === TerminalShellType.powershell || shellType === TerminalShellType.powershellCore) { + // Due to upstream bug: https://github.com/microsoft/vscode/issues/204616, assume shell integration is working for now. + return true; + } + if (!this.isWorkingForShell.has(shellType)) { + // Maybe data write event has not been processed yet, wait a bit. + await sleep(1000); + } + traceVerbose( + 'Did we determine shell integration to be working for', + shellType, + '?', + this.isWorkingForShell.has(shellType), + ); + return this.isWorkingForShell.has(shellType); + } + /** * Creates a dummy terminal so that we are guaranteed a data write event for this shell type. */ @@ -146,39 +155,8 @@ export class ShellIntegrationService implements IShellIntegrationService { hideFromUser: true, }); } +} - private async checkIfWorkingByRunningCommand(shell: string): Promise { - const shellType = identifyShellFromShellPath(shell); - const deferred = createDeferred(); - const timestamp = new Date().getTime(); - const name = `Python ${timestamp}`; - const onDidExecuteTerminalCommand = this.appShell.onDidExecuteTerminalCommand?.bind(this.appShell); - if (!onDidExecuteTerminalCommand) { - // Proposed API is not available, assume shell integration is working at this point. - return true; - } - try { - const disposable = onDidExecuteTerminalCommand((e) => { - if (e.terminal.name === name) { - deferred.resolve(); - } - }); - const terminal = this.terminalManager.createTerminal({ - name, - shellPath: shell, - hideFromUser: true, - }); - terminal.sendText(`echo ${shell}`); - const success = await Promise.race([sleep(3000).then(() => false), deferred.promise.then(() => true)]); - disposable.dispose(); - if (!success) { - traceInfo(`Shell integration is not working for ${shellType}`); - } - return success; - } catch (ex) { - traceVerbose(`Proposed API is not available, failed to subscribe to onDidExecuteTerminalCommand`, ex); - // Proposed API is not available, assume shell integration is working at this point. - return true; - } - } +function getKeyForShell(shellType: TerminalShellType) { + return `${isShellIntegrationWorking.key}_${shellType}`; } From 5f971ae83cdea6941071da9810ef6c8432c0f735 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Feb 2024 13:11:20 +0530 Subject: [PATCH 012/767] Prepend `PATH` both at shell integration and process creation (#22905) --- .../terminals/envCollectionActivation/service.ts | 14 +++++++++----- .../shellIntegrationService.ts | 3 ++- src/client/terminals/types.ts | 2 +- .../terminalEnvVarCollectionService.unit.test.ts | 12 ++++++------ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index a450c5b02162..ae591e1d7be8 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -130,7 +130,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ this.disposables, ); const { shell } = this.applicationEnvironment; - const isActive = await this.shellIntegrationService.isWorking(shell); + const isActive = await this.shellIntegrationService.isWorking(); const shellType = identifyShellFromShellPath(shell); if (!isActive && shellType !== TerminalShellType.commandPrompt) { traceWarn( @@ -218,6 +218,10 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ return; } if (key === 'PATH') { + const options = { + applyAtShellIntegration: true, + applyAtProcessCreation: true, + }; if (processEnv.PATH && env.PATH?.endsWith(processEnv.PATH)) { // Prefer prepending to PATH instead of replacing it, as we do not want to replace any // changes to PATH users might have made it in their init scripts (~/.bashrc etc.) @@ -226,7 +230,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ value = `${deactivate}${this.separator}${value}`; } traceVerbose(`Prepending environment variable ${key} in collection with ${value}`); - envVarCollection.prepend(key, value, prependOptions); + envVarCollection.prepend(key, value, options); } else { if (!value.endsWith(this.separator)) { value = value.concat(this.separator); @@ -235,7 +239,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ value = `${deactivate}${this.separator}${value}`; } traceVerbose(`Prepending environment variable ${key} in collection to ${value}`); - envVarCollection.prepend(key, value, prependOptions); + envVarCollection.prepend(key, value, options); } return; } @@ -300,7 +304,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ // PS1 should be set but no PS1 was set. return; } - const config = await this.shellIntegrationService.isWorking(shell); + const config = await this.shellIntegrationService.isWorking(); if (!config) { traceVerbose('PS1 is not set when shell integration is disabled.'); return; @@ -356,7 +360,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ } private async getPrependOptions(): Promise { - const isActive = await this.shellIntegrationService.isWorking(this.applicationEnvironment.shell); + const isActive = await this.shellIntegrationService.isWorking(); // Ideally we would want to prepend exactly once, either at shell integration or process creation. // TODO: Stop prepending altogether once https://github.com/microsoft/vscode/issues/145234 is available. return isActive diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index 9846b0c36f23..03b7d25de986 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -97,7 +97,8 @@ export class ShellIntegrationService implements IShellIntegrationService { public readonly onDidChangeStatus = this.didChange.event; - public async isWorking(shell: string): Promise { + public async isWorking(): Promise { + const { shell } = this.appEnvironment; return this._isWorking(shell).catch((ex) => { traceError(`Failed to determine if shell supports shell integration`, shell, ex); return false; diff --git a/src/client/terminals/types.ts b/src/client/terminals/types.ts index fbdd490c175c..4c73da63dd1e 100644 --- a/src/client/terminals/types.ts +++ b/src/client/terminals/types.ts @@ -45,7 +45,7 @@ export interface ITerminalEnvVarCollectionService { export const IShellIntegrationService = Symbol('IShellIntegrationService'); export interface IShellIntegrationService { onDidChangeStatus: Event; - isWorking(shell: string): Promise; + isWorking(): Promise; } export const ITerminalDeactivateService = Symbol('ITerminalDeactivateService'); diff --git a/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts b/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts index c1955c2704e6..251c0d1aabaf 100644 --- a/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts +++ b/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts @@ -75,7 +75,7 @@ suite('Terminal Environment Variable Collection Service', () => { context = mock(); shell = mock(); shellIntegrationService = mock(); - when(shellIntegrationService.isWorking(anything())).thenResolve(true); + when(shellIntegrationService.isWorking()).thenResolve(true); globalCollection = mock(); collection = mock(); when(context.environmentVariableCollection).thenReturn(instance(globalCollection)); @@ -336,7 +336,7 @@ suite('Terminal Environment Variable Collection Service', () => { verify(collection.clear()).once(); verify(collection.prepend('PATH', prependedPart, anything())).once(); verify(collection.replace('PATH', anything(), anything())).never(); - assert.deepEqual(opts, { applyAtProcessCreation: false, applyAtShellIntegration: true }); + assert.deepEqual(opts, { applyAtProcessCreation: true, applyAtShellIntegration: true }); }); test('Also prepend deactivate script location if available', async () => { @@ -372,7 +372,7 @@ suite('Terminal Environment Variable Collection Service', () => { const separator = getOSType() === OSType.Windows ? ';' : ':'; verify(collection.prepend('PATH', `scriptLocation${separator}${prependedPart}`, anything())).once(); verify(collection.replace('PATH', anything(), anything())).never(); - assert.deepEqual(opts, { applyAtProcessCreation: false, applyAtShellIntegration: true }); + assert.deepEqual(opts, { applyAtProcessCreation: true, applyAtShellIntegration: true }); }); test('Prepend full PATH with separator otherwise', async () => { @@ -405,7 +405,7 @@ suite('Terminal Environment Variable Collection Service', () => { verify(collection.clear()).once(); verify(collection.prepend('PATH', `${finalPath}${separator}`, anything())).once(); verify(collection.replace('PATH', anything(), anything())).never(); - assert.deepEqual(opts, { applyAtProcessCreation: false, applyAtShellIntegration: true }); + assert.deepEqual(opts, { applyAtProcessCreation: true, applyAtShellIntegration: true }); }); test('Prepend full PATH with separator otherwise', async () => { @@ -441,7 +441,7 @@ suite('Terminal Environment Variable Collection Service', () => { verify(collection.clear()).once(); verify(collection.prepend('PATH', `scriptLocation${separator}${finalPath}${separator}`, anything())).once(); verify(collection.replace('PATH', anything(), anything())).never(); - assert.deepEqual(opts, { applyAtProcessCreation: false, applyAtShellIntegration: true }); + assert.deepEqual(opts, { applyAtProcessCreation: true, applyAtShellIntegration: true }); }); test('Verify envs are not applied if env activation is disabled', async () => { @@ -523,7 +523,7 @@ suite('Terminal Environment Variable Collection Service', () => { test('Correct track that prompt was set for PS1 if shell integration is disabled', async () => { reset(shellIntegrationService); - when(shellIntegrationService.isWorking(anything())).thenResolve(false); + when(shellIntegrationService.isWorking()).thenResolve(false); when(platform.osType).thenReturn(OSType.Linux); const envVars: NodeJS.ProcessEnv = { VIRTUAL_ENV: 'prefix/to/venv', PS1: '(.venv)', ...process.env }; const ps1Shell = 'bash'; From 2dc158e162865f281d3a7b7b95f39705a948aeec Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Feb 2024 13:50:38 +0530 Subject: [PATCH 013/767] Log options being passed when using environment collection APIs (#22907) For https://github.com/microsoft/vscode-python/issues/22899 --- .../envCollectionActivation/service.ts | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index ae591e1d7be8..491ae786754b 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -198,7 +198,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ // PS1 in some cases is a shell variable (not an env variable) so "env" might not contain it, calculate it in that case. env.PS1 = await this.getPS1(shell, resource, env); - const prependOptions = await this.getPrependOptions(); + const defaultPrependOptions = await this.getPrependOptions(); // Clear any previously set env vars from collection envVarCollection.clear(); @@ -213,8 +213,12 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (value !== undefined) { if (key === 'PS1') { // We cannot have the full PS1 without executing in terminal, which we do not. Hence prepend it. - traceVerbose(`Prepending environment variable ${key} in collection with ${value}`); - envVarCollection.prepend(key, value, prependOptions); + traceVerbose( + `Prepending environment variable ${key} in collection with ${value} ${JSON.stringify( + defaultPrependOptions, + )}`, + ); + envVarCollection.prepend(key, value, defaultPrependOptions); return; } if (key === 'PATH') { @@ -229,7 +233,11 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (deactivate) { value = `${deactivate}${this.separator}${value}`; } - traceVerbose(`Prepending environment variable ${key} in collection with ${value}`); + traceVerbose( + `Prepending environment variable ${key} in collection with ${value} ${JSON.stringify( + options, + )}`, + ); envVarCollection.prepend(key, value, options); } else { if (!value.endsWith(this.separator)) { @@ -238,16 +246,23 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (deactivate) { value = `${deactivate}${this.separator}${value}`; } - traceVerbose(`Prepending environment variable ${key} in collection to ${value}`); + traceVerbose( + `Prepending environment variable ${key} in collection to ${value} ${JSON.stringify( + options, + )}`, + ); envVarCollection.prepend(key, value, options); } return; } - traceVerbose(`Setting environment variable ${key} in collection to ${value}`); - envVarCollection.replace(key, value, { + const options = { applyAtShellIntegration: true, applyAtProcessCreation: true, - }); + }; + traceVerbose( + `Setting environment variable ${key} in collection to ${value} ${JSON.stringify(options)}`, + ); + envVarCollection.replace(key, value, options); } } }); From 6838ccfcc5eccccc033a6872cf7c2448b349c30d Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Feb 2024 15:38:26 +0530 Subject: [PATCH 014/767] Do not activate microvenv if terminal.activateEnvironment is set to false and when not in terminal experiment (#22909) --- src/client/terminals/envCollectionActivation/service.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index 491ae786754b..aaa4ccf23bea 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -353,7 +353,16 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ private async handleMicroVenv(resource: Resource) { try { + const settings = this.configurationService.getSettings(resource); const workspaceFolder = this.getWorkspaceFolder(resource); + if (!settings.terminal.activateEnvironment) { + this.getEnvironmentVariableCollection({ workspaceFolder }).clear(); + traceVerbose( + 'Do not activate microvenv as activating environments in terminal is disabled for', + resource?.fsPath, + ); + return; + } const interpreter = await this.interpreterService.getActiveInterpreter(resource); if (interpreter?.envType === EnvironmentType.Venv) { const activatePath = path.join(path.dirname(interpreter.path), 'activate'); From 7be33ebf936ce541148f349f4d22163fc80a7dc9 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Feb 2024 20:23:31 +0530 Subject: [PATCH 015/767] Add more shell integration sequences to check for (#22911) Based on discussion with Daniel For #22440 --- .../envCollectionActivation/shellIntegrationService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index 03b7d25de986..cba2ccbc6867 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -57,7 +57,8 @@ export class ShellIntegrationService implements IShellIntegrationService { } this.appShell.onDidWriteTerminalData( (e) => { - if (e.data.includes('\x1b]633;A\x07')) { + traceVerbose(e.data); // Log this temporarily for analysis + if (e.data.includes('\x1b]633;A\x07') || e.data.includes('\x1b]133;A\x07')) { let { shell } = this.appEnvironment; if ('shellPath' in e.terminal.creationOptions && e.terminal.creationOptions.shellPath) { shell = e.terminal.creationOptions.shellPath; From 83cf53b02d7544d00abf5f77cfa07c10ba25b85d Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Feb 2024 23:33:53 +0530 Subject: [PATCH 016/767] Add "isMeasurement" for LANGUAGE_SERVER_TRIGGER_DURATION (#22912) cc/ @cwebster-99 --- src/client/telemetry/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 50464414c2ed..b2ed37939fa9 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1330,7 +1330,7 @@ export interface IEventNamePropertyMapping { */ /* __GDPR__ "language_server_trigger_duration" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karrtikr" } + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karrtikr", "isMeasurement": true } } */ [EventName.LANGUAGE_SERVER_TRIGGER_DURATION]: unknown; From 2159238886862150abc1751223a61c77011d09aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 11:10:22 -0800 Subject: [PATCH 017/767] Bump actions/setup-python from 4 to 5 in /.github/actions/build-vsix (#22602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
Release notes

Sourced from actions/setup-python's releases.

v5.0.0

What's Changed

In scope of this release, we update node version runtime from node16 to node20 (actions/setup-python#772). Besides, we update dependencies to the latest versions.

Full Changelog: https://github.com/actions/setup-python/compare/v4.8.0...v5.0.0

v4.8.0

What's Changed

In scope of this release we added support for GraalPy (actions/setup-python#694). You can use this snippet to set up GraalPy:

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
  with:
    python-version: 'graalpy-22.3'
- run: python my_script.py

Besides, the release contains such changes as:

New Contributors

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.8.0

v4.7.1

What's Changed

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.7.1

v4.7.0

In scope of this release, the support for reading python version from pyproject.toml was added (actions/setup-python#669).

      - name: Setup Python
        uses: actions/setup-python@v4
</tr></table>

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-python&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-vsix/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-vsix/action.yml b/.github/actions/build-vsix/action.yml index b632eb0a25cc..52d6d1cdbdd4 100644 --- a/.github/actions/build-vsix/action.yml +++ b/.github/actions/build-vsix/action.yml @@ -23,7 +23,7 @@ runs: # Jedi LS depends on dataclasses which is not in the stdlib in Python 3.7. - name: Use Python 3.8 for JediLSP - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 cache: 'pip' From 84734a8f62d43b9de39c8aa4f25d87a8dc03bd7e Mon Sep 17 00:00:00 2001 From: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:26:18 -0600 Subject: [PATCH 018/767] Updating installed extensions section of README.md (#22893) Adding the Python Debugger to the installed extensions section of the README. --- README.md | 7 +++++-- build/license-header.txt | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3a56a09f73ed..6ad9fd6c5e61 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,12 @@ The Python extension does offer [some support](https://github.com/microsoft/vsco ## Installed extensions -The Python extension will automatically install the [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) extension to give you the best experience when working with Python files. However, Pylance is an optional dependency, meaning the Python extension will remain fully functional if it fails to be installed. You can also [uninstall](https://code.visualstudio.com/docs/editor/extension-marketplace#_uninstall-an-extension) it at the expense of some features if you’re using a different language server. +The Python extension will automatically install the following extensions by default to provide the best Python development experience in VS Code: -Extensions installed through the marketplace are subject to the [Marketplace Terms of Use](https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf). +- [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) - to provide performant Python language support +- [Python Debugger](https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy) - to provide a seamless debug experience with debugpy + +These extensions are optional dependencies, meaning the Python extension will remain fully functional if they fail to be installed. Any or all of these extensions can be [disabled](https://code.visualstudio.com/docs/editor/extension-marketplace#_disable-an-extension) or [uninstalled](https://code.visualstudio.com/docs/editor/extension-marketplace#_uninstall-an-extension) at the expense of some features. Extensions installed through the marketplace are subject to the [Marketplace Terms of Use](https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf). ## Quick start diff --git a/build/license-header.txt b/build/license-header.txt index 2a8122642cb2..2970b03d7a1c 100644 --- a/build/license-header.txt +++ b/build/license-header.txt @@ -1,7 +1,7 @@ PLEASE NOTE: This is the license for the Python extension for Visual Studio Code. The Python extension automatically installs other extensions as optional dependencies, which can be uninstalled at any time. These extensions have separate licenses: - - The Jupyter extension is released under an MIT License: - https://marketplace.visualstudio.com/items/ms-toolsai.jupyter/license + - The Python Debugger extension is released under an MIT License: + https://marketplace.visualstudio.com/items/ms-python.debugpy/license - The Pylance extension is only available in binary form and is released under a Microsoft proprietary license, the terms of which are available here: https://marketplace.visualstudio.com/items/ms-python.vscode-pylance/license From aff0b05c26df5e1235939319736017ddaf513199 Mon Sep 17 00:00:00 2001 From: paulacamargo25 Date: Tue, 13 Feb 2024 16:07:26 -0800 Subject: [PATCH 019/767] Use python debugger in testing (#22903) closed: https://github.com/microsoft/vscode-python-debugger/issues/174 --- src/client/debugger/constants.ts | 1 + src/client/debugger/types.ts | 6 +-- src/client/testing/common/debugLauncher.ts | 6 +-- .../testing/common/debugLauncher.unit.test.ts | 42 +++++++++---------- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/client/debugger/constants.ts b/src/client/debugger/constants.ts index 08b8619ce03a..a2ac198a597d 100644 --- a/src/client/debugger/constants.ts +++ b/src/client/debugger/constants.ts @@ -4,3 +4,4 @@ 'use strict'; export const DebuggerTypeName = 'python'; +export const PythonDebuggerTypeName = 'debugpy'; diff --git a/src/client/debugger/types.ts b/src/client/debugger/types.ts index 3e884cf8f64f..80585ba7d87b 100644 --- a/src/client/debugger/types.ts +++ b/src/client/debugger/types.ts @@ -5,7 +5,7 @@ import { DebugConfiguration } from 'vscode'; import { DebugProtocol } from 'vscode-debugprotocol/lib/debugProtocol'; -import { DebuggerTypeName } from './constants'; +import { DebuggerTypeName, PythonDebuggerTypeName } from './constants'; export enum DebugOptions { RedirectOutput = 'RedirectOutput', @@ -123,14 +123,14 @@ export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments, IKnownLaunchRequestArguments, DebugConfiguration { - type: typeof DebuggerTypeName; + type: typeof DebuggerTypeName | typeof PythonDebuggerTypeName; } export interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments, IKnownAttachDebugArguments, DebugConfiguration { - type: typeof DebuggerTypeName; + type: typeof DebuggerTypeName | typeof PythonDebuggerTypeName; } export interface DebugConfigurationArguments extends LaunchRequestArguments, AttachRequestArguments {} diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index c76557699ff2..85076461f22e 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -5,7 +5,7 @@ import { IApplicationShell, IDebugService } from '../../common/application/types import { EXTENSION_ROOT_DIR } from '../../common/constants'; import * as internalScripts from '../../common/process/internal/scripts'; import { IConfigurationService, IPythonSettings } from '../../common/types'; -import { DebuggerTypeName } from '../../debugger/constants'; +import { DebuggerTypeName, PythonDebuggerTypeName } from '../../debugger/constants'; import { IDebugConfigurationResolver } from '../../debugger/extension/configuration/types'; import { DebugPurpose, LaunchRequestArguments } from '../../debugger/types'; import { IServiceContainer } from '../../ioc/types'; @@ -78,7 +78,7 @@ export class DebugLauncher implements ITestDebugLauncher { if (!debugConfig) { debugConfig = { name: 'Debug Unit Test', - type: 'python', + type: 'debugpy', request: 'test', subProcess: true, }; @@ -118,7 +118,7 @@ export class DebugLauncher implements ITestDebugLauncher { for (const cfg of configs) { if ( cfg.name && - cfg.type === DebuggerTypeName && + (cfg.type === DebuggerTypeName || cfg.type === PythonDebuggerTypeName) && (cfg.request === 'test' || (cfg as LaunchRequestArguments).purpose?.includes(DebugPurpose.DebugTest)) ) { diff --git a/src/test/testing/common/debugLauncher.unit.test.ts b/src/test/testing/common/debugLauncher.unit.test.ts index bbb65f0b2e2a..1c69bac20593 100644 --- a/src/test/testing/common/debugLauncher.unit.test.ts +++ b/src/test/testing/common/debugLauncher.unit.test.ts @@ -16,7 +16,7 @@ import { IApplicationShell, IDebugService } from '../../../client/common/applica import { EXTENSION_ROOT_DIR } from '../../../client/common/constants'; import '../../../client/common/extensions'; import { IConfigurationService, IPythonSettings } from '../../../client/common/types'; -import { DebuggerTypeName } from '../../../client/debugger/constants'; +import { PythonDebuggerTypeName } from '../../../client/debugger/constants'; import { IDebugEnvironmentVariablesService } from '../../../client/debugger/extension/configuration/resolvers/helper'; import { LaunchConfigurationResolver } from '../../../client/debugger/extension/configuration/resolvers/launch'; import { DebugOptions } from '../../../client/debugger/types'; @@ -166,7 +166,7 @@ suite('Unit Tests - Debug Launcher', () => { function getDefaultDebugConfig(): DebugConfiguration { return { name: 'Debug Unit Test', - type: DebuggerTypeName, + type: PythonDebuggerTypeName, request: 'launch', console: 'internalConsole', env: {}, @@ -329,7 +329,7 @@ suite('Unit Tests - Debug Launcher', () => { }; const expected = getDefaultDebugConfig(); expected.name = 'spam'; - setupSuccess(options, 'unittest', expected, [{ name: 'spam', type: DebuggerTypeName, request: 'test' }]); + setupSuccess(options, 'unittest', expected, [{ name: 'spam', type: PythonDebuggerTypeName, request: 'test' }]); await debugLauncher.launchDebugger(options); @@ -363,7 +363,7 @@ suite('Unit Tests - Debug Launcher', () => { }; const expected = { name: 'my tests', - type: DebuggerTypeName, + type: PythonDebuggerTypeName, request: 'launch', python: 'some/dir/bin/py3', debugAdapterPython: 'some/dir/bin/py3', @@ -388,7 +388,7 @@ suite('Unit Tests - Debug Launcher', () => { setupSuccess(options, 'unittest', expected, [ { name: 'my tests', - type: DebuggerTypeName, + type: PythonDebuggerTypeName, request: 'test', pythonPath: expected.python, stopOnEntry: expected.stopOnEntry, @@ -417,9 +417,9 @@ suite('Unit Tests - Debug Launcher', () => { const expected = getDefaultDebugConfig(); expected.name = 'spam1'; setupSuccess(options, 'unittest', expected, [ - { name: 'spam1', type: DebuggerTypeName, request: 'test' }, - { name: 'spam2', type: DebuggerTypeName, request: 'test' }, - { name: 'spam3', type: DebuggerTypeName, request: 'test' }, + { name: 'spam1', type: PythonDebuggerTypeName, request: 'test' }, + { name: 'spam2', type: PythonDebuggerTypeName, request: 'test' }, + { name: 'spam3', type: PythonDebuggerTypeName, request: 'test' }, ]); await debugLauncher.launchDebugger(options); @@ -446,7 +446,7 @@ suite('Unit Tests - Debug Launcher', () => { '// test 2 \n\ { \n\ "name": "spam", \n\ - "type": "python", \n\ + "type": "debugpy", \n\ "request": "test" \n\ } \n\ ', @@ -454,7 +454,7 @@ suite('Unit Tests - Debug Launcher', () => { [ \n\ { \n\ "name": "spam", \n\ - "type": "python", \n\ + "type": "debugpy", \n\ "request": "test" \n\ } \n\ ] \n\ @@ -464,7 +464,7 @@ suite('Unit Tests - Debug Launcher', () => { "configurations": [ \n\ { \n\ "name": "spam", \n\ - "type": "python", \n\ + "type": "debugpy", \n\ "request": "test" \n\ } \n\ ] \n\ @@ -499,10 +499,10 @@ suite('Unit Tests - Debug Launcher', () => { setupSuccess(options, 'unittest', expected, [ {} as DebugConfiguration, { name: 'spam1' } as DebugConfiguration, - { name: 'spam2', type: DebuggerTypeName } as DebugConfiguration, + { name: 'spam2', type: PythonDebuggerTypeName } as DebugConfiguration, { name: 'spam3', request: 'test' } as DebugConfiguration, - { type: DebuggerTypeName } as DebugConfiguration, - { type: DebuggerTypeName, request: 'test' } as DebugConfiguration, + { type: PythonDebuggerTypeName } as DebugConfiguration, + { type: PythonDebuggerTypeName, request: 'test' } as DebugConfiguration, { request: 'test' } as DebugConfiguration, ]); @@ -532,7 +532,7 @@ suite('Unit Tests - Debug Launcher', () => { testProvider: 'unittest', }; const expected = getDefaultDebugConfig(); - setupSuccess(options, 'unittest', expected, [{ name: 'spam', type: DebuggerTypeName, request: 'bogus' }]); + setupSuccess(options, 'unittest', expected, [{ name: 'spam', type: PythonDebuggerTypeName, request: 'bogus' }]); await debugLauncher.launchDebugger(options); @@ -547,8 +547,8 @@ suite('Unit Tests - Debug Launcher', () => { }; const expected = getDefaultDebugConfig(); setupSuccess(options, 'unittest', expected, [ - { name: 'spam', type: DebuggerTypeName, request: 'launch' }, - { name: 'spam', type: DebuggerTypeName, request: 'attach' }, + { name: 'spam', type: PythonDebuggerTypeName, request: 'launch' }, + { name: 'spam', type: PythonDebuggerTypeName, request: 'attach' }, ]); await debugLauncher.launchDebugger(options); @@ -567,9 +567,9 @@ suite('Unit Tests - Debug Launcher', () => { setupSuccess(options, 'unittest', expected, [ { name: 'foo1', type: 'other', request: 'bar' }, { name: 'foo2', type: 'other', request: 'bar' }, - { name: 'spam1', type: DebuggerTypeName, request: 'launch' }, - { name: 'spam2', type: DebuggerTypeName, request: 'test' }, - { name: 'spam3', type: DebuggerTypeName, request: 'attach' }, + { name: 'spam1', type: PythonDebuggerTypeName, request: 'launch' }, + { name: 'spam2', type: PythonDebuggerTypeName, request: 'test' }, + { name: 'spam3', type: PythonDebuggerTypeName, request: 'attach' }, { name: 'xyz', type: 'another', request: 'abc' }, ]); @@ -599,7 +599,7 @@ suite('Unit Tests - Debug Launcher', () => { { \n\ // "test" debug config \n\ "name": "spam", /* non-empty */ \n\ - "type": "python", /* must be "python" */ \n\ + "type": "debugpy", /* must be "python" */ \n\ "request": "test", /* must be "test" */ \n\ // extra stuff here: \n\ "stopOnEntry": true \n\ From a60fbd5617d4a3081f4fc9af07a1bbd4e2003cc4 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:33:59 -0800 Subject: [PATCH 020/767] Add GDPR tags for new Pylance properties (#22922) --- src/client/telemetry/pylance.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/telemetry/pylance.ts b/src/client/telemetry/pylance.ts index afa6b5298084..67be7428c7be 100644 --- a/src/client/telemetry/pylance.ts +++ b/src/client/telemetry/pylance.ts @@ -118,6 +118,8 @@ "custom_completionitemtelemetrybuildtimeinms" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "custom_extensiontotaltimeinms" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "custom_selecteditemtelemetrybuildtimeinms" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "custom_completiontype" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "custom_filetype" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "lsversion" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "parsecallcount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "parsetime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, From e53651d75114cc272df532d5eab9f4ccdf1e0e92 Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:40:45 -0800 Subject: [PATCH 021/767] Prevent first Python command being lost (#22902) Fixes: #22673 Fixes: #22545 Fixes: #22691 Making best effort to address issue where very first command sent to REPL via Terminal gets ignored, or gets pasted both in Terminal and in REPL. With the fix, we observe whether Python REPL is launched in Terminal via VS Code's `onDidWriteTerminalData` and send the command, or wait three seconds as a fallback mechanism. These two combined together will significantly reduce or resolve all-together the chance of very first command being swollen up or gets pasted twice in Terminal and REPL previously where it did not have context of whether Python REPL instance have started inside the Terminal or not. --- .../codeExecution/djangoShellCodeExecution.ts | 9 +++- src/client/terminals/codeExecution/repl.ts | 4 +- .../codeExecution/terminalCodeExecution.ts | 35 +++++++++++-- .../djangoShellCodeExect.unit.test.ts | 10 +++- .../terminalCodeExec.unit.test.ts | 52 ++++++++----------- 5 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/client/terminals/codeExecution/djangoShellCodeExecution.ts b/src/client/terminals/codeExecution/djangoShellCodeExecution.ts index 67b877429a2e..05a1470b5727 100644 --- a/src/client/terminals/codeExecution/djangoShellCodeExecution.ts +++ b/src/client/terminals/codeExecution/djangoShellCodeExecution.ts @@ -6,7 +6,12 @@ import { inject, injectable } from 'inversify'; import * as path from 'path'; import { Disposable, Uri } from 'vscode'; -import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../common/application/types'; +import { + IApplicationShell, + ICommandManager, + IDocumentManager, + IWorkspaceService, +} from '../../common/application/types'; import '../../common/extensions'; import { IFileSystem, IPlatformService } from '../../common/platform/types'; import { ITerminalServiceFactory } from '../../common/terminal/types'; @@ -28,6 +33,7 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi @inject(IFileSystem) fileSystem: IFileSystem, @inject(IDisposableRegistry) disposableRegistry: Disposable[], @inject(IInterpreterService) interpreterService: IInterpreterService, + @inject(IApplicationShell) applicationShell: IApplicationShell, ) { super( terminalServiceFactory, @@ -37,6 +43,7 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi platformService, interpreterService, commandManager, + applicationShell, ); this.terminalTitle = 'Django Shell'; disposableRegistry.push(new DjangoContextInitializer(documentManager, workspace, fileSystem, commandManager)); diff --git a/src/client/terminals/codeExecution/repl.ts b/src/client/terminals/codeExecution/repl.ts index 45f19798c3d8..bc9a30af1fac 100644 --- a/src/client/terminals/codeExecution/repl.ts +++ b/src/client/terminals/codeExecution/repl.ts @@ -5,7 +5,7 @@ import { inject, injectable } from 'inversify'; import { Disposable } from 'vscode'; -import { ICommandManager, IWorkspaceService } from '../../common/application/types'; +import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../common/application/types'; import { IPlatformService } from '../../common/platform/types'; import { ITerminalServiceFactory } from '../../common/terminal/types'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; @@ -22,6 +22,7 @@ export class ReplProvider extends TerminalCodeExecutionProvider { @inject(IPlatformService) platformService: IPlatformService, @inject(IInterpreterService) interpreterService: IInterpreterService, @inject(ICommandManager) commandManager: ICommandManager, + @inject(IApplicationShell) applicationShell: IApplicationShell, ) { super( terminalServiceFactory, @@ -31,6 +32,7 @@ export class ReplProvider extends TerminalCodeExecutionProvider { platformService, interpreterService, commandManager, + applicationShell, ); this.terminalTitle = 'REPL'; } diff --git a/src/client/terminals/codeExecution/terminalCodeExecution.ts b/src/client/terminals/codeExecution/terminalCodeExecution.ts index a257fff20dbf..4d775dbf6f97 100644 --- a/src/client/terminals/codeExecution/terminalCodeExecution.ts +++ b/src/client/terminals/codeExecution/terminalCodeExecution.ts @@ -6,11 +6,11 @@ import { inject, injectable } from 'inversify'; import * as path from 'path'; import { Disposable, Uri } from 'vscode'; -import { ICommandManager, IWorkspaceService } from '../../common/application/types'; +import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../common/application/types'; import '../../common/extensions'; import { IPlatformService } from '../../common/platform/types'; import { ITerminalService, ITerminalServiceFactory } from '../../common/terminal/types'; -import { IConfigurationService, IDisposableRegistry, Resource } from '../../common/types'; +import { IConfigurationService, IDisposable, IDisposableRegistry, Resource } from '../../common/types'; import { Diagnostics, Repl } from '../../common/utils/localize'; import { showWarningMessage } from '../../common/vscodeApis/windowApis'; import { IInterpreterService } from '../../interpreter/contracts'; @@ -30,6 +30,7 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService { @inject(IPlatformService) protected readonly platformService: IPlatformService, @inject(IInterpreterService) protected readonly interpreterService: IInterpreterService, @inject(ICommandManager) protected readonly commandManager: ICommandManager, + @inject(IApplicationShell) protected readonly applicationShell: IApplicationShell, ) {} public async executeFile(file: Uri, options?: { newTerminalPerFile: boolean }) { @@ -65,10 +66,34 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService { } this.replActive = new Promise(async (resolve) => { const replCommandArgs = await this.getExecutableInfo(resource); + let listener: IDisposable; + Promise.race([ + new Promise((resolve) => setTimeout(() => resolve(true), 3000)), + new Promise((resolve) => { + let count = 0; + const terminalDataTimeout = setTimeout(() => { + resolve(true); // Fall back for test case scenarios. + }, 3000); + // Watch TerminalData to see if REPL launched. + listener = this.applicationShell.onDidWriteTerminalData((e) => { + for (let i = 0; i < e.data.length; i++) { + if (e.data[i] === '>') { + count++; + if (count === 3) { + clearTimeout(terminalDataTimeout); + resolve(true); + } + } + } + }); + }), + ]).then(() => { + if (listener) { + listener.dispose(); + } + resolve(true); + }); terminalService.sendCommand(replCommandArgs.command, replCommandArgs.args); - - // Give python repl time to start before we start sending text. - setTimeout(() => resolve(true), 1000); }); this.disposables.push( terminalService.onDidCloseTerminal(() => { diff --git a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts index 6c6cf5baec76..749d94672765 100644 --- a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts +++ b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts @@ -6,7 +6,12 @@ import * as path from 'path'; import * as TypeMoq from 'typemoq'; import * as sinon from 'sinon'; import { Disposable, Uri, WorkspaceFolder } from 'vscode'; -import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../client/common/application/types'; +import { + IApplicationShell, + ICommandManager, + IDocumentManager, + IWorkspaceService, +} from '../../../client/common/application/types'; import { IFileSystem, IPlatformService } from '../../../client/common/platform/types'; import { createCondaEnv } from '../../../client/common/process/pythonEnvironment'; import { createPythonProcessService } from '../../../client/common/process/pythonProcess'; @@ -32,12 +37,14 @@ suite('Terminal - Django Shell Code Execution', () => { let settings: TypeMoq.IMock; let interpreterService: TypeMoq.IMock; let pythonExecutionFactory: TypeMoq.IMock; + let applicationShell: TypeMoq.IMock; let disposables: Disposable[] = []; setup(() => { const terminalFactory = TypeMoq.Mock.ofType(); terminalSettings = TypeMoq.Mock.ofType(); terminalService = TypeMoq.Mock.ofType(); const configService = TypeMoq.Mock.ofType(); + applicationShell = TypeMoq.Mock.ofType(); workspace = TypeMoq.Mock.ofType(); workspace .setup((c) => c.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) @@ -62,6 +69,7 @@ suite('Terminal - Django Shell Code Execution', () => { fileSystem.object, disposables, interpreterService.object, + applicationShell.object, ); terminalFactory.setup((f) => f.getTerminalService(TypeMoq.It.isAny())).returns(() => terminalService.object); diff --git a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts index 93845e6189eb..4f60adb3b931 100644 --- a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts +++ b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts @@ -6,7 +6,12 @@ import * as path from 'path'; import { SemVer } from 'semver'; import * as TypeMoq from 'typemoq'; import { Disposable, Uri, WorkspaceFolder } from 'vscode'; -import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../client/common/application/types'; +import { + IApplicationShell, + ICommandManager, + IDocumentManager, + IWorkspaceService, +} from '../../../client/common/application/types'; import { IFileSystem, IPlatformService } from '../../../client/common/platform/types'; import { createCondaEnv } from '../../../client/common/process/pythonEnvironment'; import { createPythonProcessService } from '../../../client/common/process/pythonProcess'; @@ -47,6 +52,7 @@ suite('Terminal - Code Execution', () => { let pythonExecutionFactory: TypeMoq.IMock; let interpreterService: TypeMoq.IMock; let isDjangoRepl: boolean; + let applicationShell: TypeMoq.IMock; teardown(() => { disposables.forEach((disposable) => { @@ -71,6 +77,7 @@ suite('Terminal - Code Execution', () => { fileSystem = TypeMoq.Mock.ofType(); pythonExecutionFactory = TypeMoq.Mock.ofType(); interpreterService = TypeMoq.Mock.ofType(); + applicationShell = TypeMoq.Mock.ofType(); settings = TypeMoq.Mock.ofType(); settings.setup((s) => s.terminal).returns(() => terminalSettings.object); configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); @@ -85,6 +92,7 @@ suite('Terminal - Code Execution', () => { platform.object, interpreterService.object, commandManager.object, + applicationShell.object, ); break; } @@ -97,6 +105,7 @@ suite('Terminal - Code Execution', () => { platform.object, interpreterService.object, commandManager.object, + applicationShell.object, ); expectedTerminalTitle = 'REPL'; break; @@ -120,6 +129,7 @@ suite('Terminal - Code Execution', () => { fileSystem.object, disposables, interpreterService.object, + applicationShell.object, ); expectedTerminalTitle = 'Django Shell'; break; @@ -590,7 +600,7 @@ suite('Terminal - Code Execution', () => { ); }); - test('Ensure repl is re-initialized when terminal is closed', async () => { + test('Ensure REPL launches after reducing risk of command being ignored or duplicated', async () => { const pythonPath = 'usr/bin/python1234'; const terminalArgs = ['-a', 'b', 'c']; platform.setup((p) => p.isWindows).returns(() => false); @@ -599,43 +609,27 @@ suite('Terminal - Code Execution', () => { .returns(() => Promise.resolve(({ path: pythonPath } as unknown) as PythonEnvironment)); terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs); - let closeTerminalCallback: undefined | (() => void); - terminalService - .setup((t) => t.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns((callback) => { - closeTerminalCallback = callback; - return { - dispose: noop, - }; - }); - await executor.execute('cmd1'); await executor.execute('cmd2'); await executor.execute('cmd3'); - const expectedTerminalArgs = isDjangoRepl ? terminalArgs.concat(['manage.py', 'shell']) : terminalArgs; - - expect(closeTerminalCallback).not.to.be.an('undefined', 'Callback not initialized'); - terminalService.verify( - async (t) => - t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), - TypeMoq.Times.once(), + // Now check if sendCommand from the initializeRepl is called atLeastOnce. + // This is due to newly added Promise race and fallback to lower risk of swollen first command. + applicationShell.verify( + async (t) => t.onDidWriteTerminalData(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + TypeMoq.Times.atLeastOnce(), ); - closeTerminalCallback!.call(terminalService.object); await executor.execute('cmd4'); - terminalService.verify( - async (t) => - t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), - TypeMoq.Times.exactly(2), + applicationShell.verify( + async (t) => t.onDidWriteTerminalData(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + TypeMoq.Times.atLeastOnce(), ); - closeTerminalCallback!.call(terminalService.object); await executor.execute('cmd5'); - terminalService.verify( - async (t) => - t.sendCommand(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isValue(expectedTerminalArgs)), - TypeMoq.Times.exactly(3), + applicationShell.verify( + async (t) => t.onDidWriteTerminalData(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + TypeMoq.Times.atLeastOnce(), ); }); From 75ed73e8b139f5f785e8292fac047ae23185ca63 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 19 Feb 2024 19:05:24 -0800 Subject: [PATCH 022/767] Fix Bug with Pytest when using symlinked workspaces (#22885) fixes https://github.com/microsoft/vscode-python/issues/22658 also implements switching to arg mapping which is this issue here: https://github.com/microsoft/vscode-python/issues/22076 --------- Co-authored-by: Karthik Nadig --- ThirdPartyNotices-Repository.txt | 29 +++ .../expected_discovery_test_output.py | 75 ++++++++ pythonFiles/tests/pytestadapter/helpers.py | 18 ++ .../tests/pytestadapter/test_discovery.py | 42 ++++- pythonFiles/vscode_pytest/__init__.py | 53 +++++- .../testing/testController/common/utils.ts | 80 ++++++++ .../pytest/pytestDiscoveryAdapter.ts | 19 +- .../pytest/pytestExecutionAdapter.ts | 21 ++- .../testing/common/testingAdapter.test.ts | 113 ++++++++++++ .../pytestDiscoveryAdapter.unit.test.ts | 68 +++++++ .../pytestExecutionAdapter.unit.test.ts | 7 +- .../testing/testController/utils.unit.test.ts | 174 ++++++++++++++++++ 12 files changed, 681 insertions(+), 18 deletions(-) diff --git a/ThirdPartyNotices-Repository.txt b/ThirdPartyNotices-Repository.txt index 9e7e822af1bb..bbb00d523f91 100644 --- a/ThirdPartyNotices-Repository.txt +++ b/ThirdPartyNotices-Repository.txt @@ -17,6 +17,7 @@ Microsoft Python extension for Visual Studio Code incorporates third party mater 11. vscode-cpptools (https://github.com/microsoft/vscode-cpptools) 12. mocha (https://github.com/mochajs/mocha) 13. get-pip (https://github.com/pypa/get-pip) +14. vscode-js-debug (https://github.com/microsoft/vscode-js-debug) %% Go for Visual Studio Code NOTICES, INFORMATION, AND LICENSE BEGIN HERE @@ -1032,3 +1033,31 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF get-pip NOTICES, INFORMATION, AND LICENSE + + +%% vscode-js-debug NOTICES, INFORMATION, AND LICENSE BEGIN HERE +========================================= + +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE +========================================= +END OF vscode-js-debug NOTICES, INFORMATION, AND LICENSE diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index 2d86710e776b..7fbb0c5c43e7 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -994,3 +994,78 @@ ], "id_": str(TEST_DATA_PATH), } +SYMLINK_FOLDER_PATH = TEST_DATA_PATH / "symlink_folder" +SYMLINK_FOLDER_PATH_TESTS = TEST_DATA_PATH / "symlink_folder" / "tests" +SYMLINK_FOLDER_PATH_TESTS_TEST_A = ( + TEST_DATA_PATH / "symlink_folder" / "tests" / "test_a.py" +) +SYMLINK_FOLDER_PATH_TESTS_TEST_B = ( + TEST_DATA_PATH / "symlink_folder" / "tests" / "test_b.py" +) + +symlink_expected_discovery_output = { + "name": "symlink_folder", + "path": str(SYMLINK_FOLDER_PATH), + "type_": "folder", + "children": [ + { + "name": "tests", + "path": str(SYMLINK_FOLDER_PATH_TESTS), + "type_": "folder", + "id_": str(SYMLINK_FOLDER_PATH_TESTS), + "children": [ + { + "name": "test_a.py", + "path": str(SYMLINK_FOLDER_PATH_TESTS_TEST_A), + "type_": "file", + "id_": str(SYMLINK_FOLDER_PATH_TESTS_TEST_A), + "children": [ + { + "name": "test_a_function", + "path": str(SYMLINK_FOLDER_PATH_TESTS_TEST_A), + "lineno": find_test_line_number( + "test_a_function", + os.path.join(tests_path, "test_a.py"), + ), + "type_": "test", + "id_": get_absolute_test_id( + "tests/test_a.py::test_a_function", + SYMLINK_FOLDER_PATH_TESTS_TEST_A, + ), + "runID": get_absolute_test_id( + "tests/test_a.py::test_a_function", + SYMLINK_FOLDER_PATH_TESTS_TEST_A, + ), + } + ], + }, + { + "name": "test_b.py", + "path": str(SYMLINK_FOLDER_PATH_TESTS_TEST_B), + "type_": "file", + "id_": str(SYMLINK_FOLDER_PATH_TESTS_TEST_B), + "children": [ + { + "name": "test_b_function", + "path": str(SYMLINK_FOLDER_PATH_TESTS_TEST_B), + "lineno": find_test_line_number( + "test_b_function", + os.path.join(tests_path, "test_b.py"), + ), + "type_": "test", + "id_": get_absolute_test_id( + "tests/test_b.py::test_b_function", + SYMLINK_FOLDER_PATH_TESTS_TEST_B, + ), + "runID": get_absolute_test_id( + "tests/test_b.py::test_b_function", + SYMLINK_FOLDER_PATH_TESTS_TEST_B, + ), + } + ], + }, + ], + } + ], + "id_": str(SYMLINK_FOLDER_PATH), +} diff --git a/pythonFiles/tests/pytestadapter/helpers.py b/pythonFiles/tests/pytestadapter/helpers.py index 2d36da59956b..a3ed21cc5538 100644 --- a/pythonFiles/tests/pytestadapter/helpers.py +++ b/pythonFiles/tests/pytestadapter/helpers.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import contextlib import io import json import os @@ -27,6 +28,23 @@ def get_absolute_test_id(test_id: str, testPath: pathlib.Path) -> str: return absolute_test_id +@contextlib.contextmanager +def create_symlink(root: pathlib.Path, target_ext: str, destination_ext: str): + try: + destination = root / destination_ext + target = root / target_ext + if destination.exists(): + print("destination already exists", destination) + try: + destination.symlink_to(target) + except Exception as e: + print("error occurred when attempting to create a symlink", e) + yield target, destination + finally: + destination.unlink() + print("destination unlinked", destination) + + def create_server( host: str = "127.0.0.1", port: int = 0, diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 9956b82a6345..b28a2b307ae2 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import os import pathlib import shutil @@ -14,7 +15,7 @@ from tests.tree_comparison_helper import is_same_tree from . import expected_discovery_test_output -from .helpers import TEST_DATA_PATH, runner, runner_with_cwd +from .helpers import TEST_DATA_PATH, runner, runner_with_cwd, create_symlink def test_import_error(tmp_path): @@ -205,6 +206,45 @@ def test_pytest_collect(file, expected_const): assert is_same_tree(actual_item.get("tests"), expected_const) +def test_symlink_root_dir(): + """ + Test to test pytest discovery with the command line arg --rootdir specified as a symlink path. + Discovery should succeed and testids should be relative to the symlinked root directory. + """ + with create_symlink(TEST_DATA_PATH, "root", "symlink_folder") as ( + source, + destination, + ): + assert destination.is_symlink() + + # Run pytest with the cwd being the resolved symlink path (as it will be when we run the subprocess from node). + actual = runner_with_cwd( + ["--collect-only", f"--rootdir={os.fspath(destination)}"], source + ) + expected = expected_discovery_test_output.symlink_expected_discovery_output + assert actual + actual_list: List[Dict[str, Any]] = actual + if actual_list is not None: + assert actual_list.pop(-1).get("eot") + actual_item = actual_list.pop(0) + try: + # Check if all requirements + assert all( + item in actual_item.keys() for item in ("status", "cwd", "error") + ), "Required keys are missing" + assert actual_item.get("status") == "success", "Status is not 'success'" + assert actual_item.get("cwd") == os.fspath( + destination + ), f"CWD does not match: {os.fspath(destination)}" + assert ( + actual_item.get("tests") == expected + ), "Tests do not match expected value" + except AssertionError as e: + # Print the actual_item in JSON format if an assertion fails + print(json.dumps(actual_item, indent=4)) + pytest.fail(str(e)) + + def test_pytest_root_dir(): """ Test to test pytest discovery with the command line arg --rootdir specified to be a subfolder diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index a565fbf930a6..256f2bfdb099 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -57,6 +57,7 @@ def __init__(self, message): collected_tests_so_far = list() TEST_PORT = os.getenv("TEST_PORT") TEST_UUID = os.getenv("TEST_UUID") +SYMLINK_PATH = None def pytest_load_initial_conftests(early_config, parser, args): @@ -75,6 +76,25 @@ def pytest_load_initial_conftests(early_config, parser, args): global IS_DISCOVERY IS_DISCOVERY = True + # check if --rootdir is in the args + for arg in args: + if "--rootdir=" in arg: + rootdir = arg.split("--rootdir=")[1] + if not os.path.exists(rootdir): + raise VSCodePytestError( + f"The path set in the argument --rootdir={rootdir} does not exist." + ) + if ( + os.path.islink(rootdir) + and pathlib.Path(os.path.realpath(rootdir)) == pathlib.Path.cwd() + ): + print( + f"Plugin info[vscode-pytest]: rootdir argument, {rootdir}, is identified as a symlink to the cwd, {pathlib.Path.cwd()}.", + "Therefore setting symlink path to rootdir argument.", + ) + global SYMLINK_PATH + SYMLINK_PATH = pathlib.Path(rootdir) + def pytest_internalerror(excrepr, excinfo): """A pytest hook that is called when an internal error occurs. @@ -326,6 +346,13 @@ def pytest_sessionfinish(session, exitstatus): Exit code 5: No tests were collected """ cwd = pathlib.Path.cwd() + if SYMLINK_PATH: + print("Plugin warning[vscode-pytest]: SYMLINK set, adjusting cwd.") + # Get relative between the cwd (resolved path) and the node path. + rel_path = os.path.relpath(cwd, pathlib.Path.cwd()) + # Calculate the new node path by making it relative to the symlink path. + cwd = pathlib.Path(os.path.join(SYMLINK_PATH, rel_path)) + if IS_DISCOVERY: if not (exitstatus == 0 or exitstatus == 1 or exitstatus == 5): errorNode: TestNode = { @@ -388,6 +415,11 @@ def build_test_tree(session: pytest.Session) -> TestNode: class_nodes_dict: Dict[str, TestNode] = {} function_nodes_dict: Dict[str, TestNode] = {} + # Check to see if the global variable for symlink path is set + if SYMLINK_PATH: + session_node["path"] = SYMLINK_PATH + session_node["id_"] = os.fspath(SYMLINK_PATH) + for test_case in session.items: test_node = create_test_node(test_case) if isinstance(test_case.parent, pytest.Class): @@ -645,13 +677,31 @@ class EOTPayloadDict(TypedDict): def get_node_path(node: Any) -> pathlib.Path: - """A function that returns the path of a node given the switch to pathlib.Path.""" + """ + A function that returns the path of a node given the switch to pathlib.Path. + It also evaluates if the node is a symlink and returns the equivalent path. + """ path = getattr(node, "path", None) or pathlib.Path(node.fspath) if not path: raise VSCodePytestError( f"Unable to find path for node: {node}, node.path: {node.path}, node.fspath: {node.fspath}" ) + + # Check for the session node since it has the symlink already. + if SYMLINK_PATH and not isinstance(node, pytest.Session): + # Get relative between the cwd (resolved path) and the node path. + try: + rel_path = path.relative_to(pathlib.Path.cwd()) + + # Calculate the new node path by making it relative to the symlink path. + sym_path = pathlib.Path(os.path.join(SYMLINK_PATH, rel_path)) + return sym_path + except Exception as e: + raise VSCodePytestError( + f"Error occurred while calculating symlink equivalent from node path: {e}" + "\n SYMLINK_PATH: {SYMLINK_PATH}, \n node path: {path}, \n cwd: {{pathlib.Path.cwd()}}" + ) return path @@ -687,7 +737,6 @@ def post_response(cwd: str, session_node: TestNode) -> None: cwd (str): Current working directory. session_node (TestNode): Node information of the test session. """ - payload: DiscoveryPayloadDict = { "cwd": cwd, "status": "success" if not ERRORS else "error", diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 23ee881a405a..0e81154a899c 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -349,3 +349,83 @@ export function splitTestNameWithRegex(testName: string): [string, string] { } return [testName, testName]; } + +/** + * Converts an array of strings (with or without '=') into a map. + * If a string contains '=', it is split into a key-value pair, with the portion + * before the '=' as the key and the portion after the '=' as the value. + * If no '=' is found in the string, the entire string becomes a key with a value of null. + * + * @param args - Readonly array of strings to be converted to a map. + * @returns A map representation of the input strings. + */ +export const argsToMap = (args: ReadonlyArray): { [key: string]: string | null | undefined } => { + const map: { [key: string]: string | null } = {}; + for (const arg of args) { + const delimiter = arg.indexOf('='); + if (delimiter === -1) { + map[arg] = null; + } else { + map[arg.slice(0, delimiter)] = arg.slice(delimiter + 1); + } + } + + return map; +}; + +/** + * Converts a map into an array of strings. + * Each key-value pair in the map is transformed into a string. + * If the value is null, only the key is represented in the string. + * If the value is defined (and not null), the string is in the format "key=value". + * If a value is undefined, the key-value pair is skipped. + * + * @param map - The map to be converted to an array of strings. + * @returns An array of strings representation of the input map. + */ +export const mapToArgs = (map: { [key: string]: string | null | undefined }): string[] => { + const out: string[] = []; + for (const key of Object.keys(map)) { + const value = map[key]; + if (value === undefined) { + // eslint-disable-next-line no-continue + continue; + } + + out.push(value === null ? key : `${key}=${value}`); + } + + return out; +}; + +/** + * Adds an argument to the map only if it doesn't already exist. + * + * @param map - The map of arguments. + * @param argKey - The argument key to be checked and added. + * @param argValue - The value to set for the argument if it's not already in the map. + * @returns The updated map. + */ +export function addArgIfNotExist( + map: { [key: string]: string | null | undefined }, + argKey: string, + argValue: string | null, +): { [key: string]: string | null | undefined } { + // Only add the argument if it doesn't exist in the map. + if (map[argKey] === undefined) { + map[argKey] = argValue; + } + + return map; +} + +/** + * Checks if an argument key exists in the map. + * + * @param map - The map of arguments. + * @param argKey - The argument key to be checked. + * @returns True if the argument key exists in the map, false otherwise. + */ +export function argKeyExists(map: { [key: string]: string | null | undefined }, argKey: string): boolean { + return map[argKey] !== undefined; +} diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 39dc87ed12c1..ab44c96821e5 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import * as path from 'path'; import { Uri } from 'vscode'; +import * as fs from 'fs'; import { ExecutionFactoryCreateWithEnvironmentOptions, IPythonExecutionFactory, @@ -10,7 +11,7 @@ import { import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { Deferred, createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceError, traceInfo, traceVerbose } from '../../../logging'; +import { traceError, traceInfo, traceVerbose, traceWarn } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -24,6 +25,9 @@ import { createEOTPayload, createTestingDeferred, fixLogLinesNoTrailing, + argsToMap, + addArgIfNotExist, + mapToArgs, } from '../common/utils'; import { IEnvironmentVariablesProvider } from '../../../common/variables/types'; @@ -66,9 +70,18 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const relativePathToPytest = 'pythonFiles'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); const settings = this.configSettings.getSettings(uri); - const { pytestArgs } = settings.testing; + let pytestArgsMap = argsToMap(settings.testing.pytestArgs); const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; + // check for symbolic path + const stats = fs.lstatSync(cwd); + if (stats.isSymbolicLink()) { + traceWarn( + "The cwd is a symbolic link, adding '--rootdir' to pytestArgsMap only if it doesn't already exist.", + ); + pytestArgsMap = addArgIfNotExist(pytestArgsMap, '--rootdir', cwd); + } + // get and edit env vars const mutableEnv = { ...(await this.envVarsService?.getEnvironmentVariables(uri)), @@ -98,7 +111,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); + const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(mapToArgs(pytestArgsMap)); traceVerbose(`Running pytest discovery with command: ${execArgs.join(' ')} for workspace ${uri.fsPath}.`); const deferredTillExecClose: Deferred = createTestingDeferred(); diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 8995a182a774..d366bdfc9718 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -129,15 +129,16 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { try { // Remove positional test folders and files, we will add as needed per node const testArgs = removePositionalFoldersAndFiles(pytestArgs); + let testArgsMap = utils.argsToMap(testArgs); // if user has provided `--rootdir` then use that, otherwise add `cwd` - if (testArgs.filter((a) => a.startsWith('--rootdir')).length === 0) { - // Make sure root dir is set so pytest can find the relative paths - testArgs.splice(0, 0, '--rootdir', uri.fsPath); - } + // root dir is required so pytest can find the relative paths and for symlinks + utils.addArgIfNotExist(testArgsMap, '--rootdir', cwd); - if (debugBool && !testArgs.some((a) => a.startsWith('--capture') || a === '-s')) { - testArgs.push('--capture', 'no'); + // -s and --capture are both command line options that control how pytest captures output. + // if neither are set, then set --capture=no to prevent pytest from capturing output. + if (debugBool && !utils.argKeyExists(testArgsMap, '-s')) { + testArgsMap = utils.addArgIfNotExist(testArgsMap, '--capture', 'no'); } // add port with run test ids to env vars @@ -162,7 +163,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const pytestUUID = uuid.toString(); const launchOptions: LaunchOptions = { cwd, - args: testArgs, + args: utils.mapToArgs(testArgsMap), token: spawnOptions.token, testProvider: PYTEST_PROVIDER, pytestPort, @@ -170,7 +171,9 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { runTestIdsPort: pytestRunTestIdsPort.toString(), }; traceInfo( - `Running DEBUG pytest with arguments: ${testArgs.join(' ')} for workspace ${uri.fsPath} \r\n`, + `Running DEBUG pytest with arguments: ${utils.mapToArgs(testArgsMap).join(' ')} for workspace ${ + uri.fsPath + } \r\n`, ); await debugLauncher!.launchDebugger(launchOptions, () => { deferredTillEOT?.resolve(); @@ -180,7 +183,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const deferredTillExecClose: Deferred = utils.createTestingDeferred(); // combine path to run script with run args const scriptPath = path.join(fullPluginPath, 'vscode_pytest', 'run_pytest_script.py'); - const runArgs = [scriptPath, ...testArgs]; + const runArgs = [scriptPath, ...utils.mapToArgs(testArgsMap)]; traceInfo(`Running pytest with arguments: ${runArgs.join(' ')} for workspace ${uri.fsPath} \r\n`); let resultProc: ChildProcess | undefined; diff --git a/src/test/testing/common/testingAdapter.test.ts b/src/test/testing/common/testingAdapter.test.ts index a9ed25194fa9..8e2f6200003e 100644 --- a/src/test/testing/common/testingAdapter.test.ts +++ b/src/test/testing/common/testingAdapter.test.ts @@ -5,6 +5,7 @@ import { TestController, TestRun, Uri } from 'vscode'; import * as typeMoq from 'typemoq'; import * as path from 'path'; import * as assert from 'assert'; +import * as fs from 'fs'; import { PytestTestDiscoveryAdapter } from '../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; import { ITestController, ITestResultResolver } from '../../../client/testing/testController/common/types'; import { PythonTestServer } from '../../../client/testing/testController/common/server'; @@ -59,8 +60,25 @@ suite('End to End Tests: test adapters', () => { 'testTestingRootWkspc', 'discoveryErrorWorkspace', ); + const rootPathDiscoverySymlink = path.join( + EXTENSION_ROOT_DIR_FOR_TESTS, + 'src', + 'testTestingRootWkspc', + 'symlinkWorkspace', + ); suiteSetup(async () => { serviceContainer = (await initialize()).serviceContainer; + + // create symlink for specific symlink test + const target = rootPathSmallWorkspace; + const dest = rootPathDiscoverySymlink; + fs.symlink(target, dest, 'dir', (err) => { + if (err) { + console.error(err); + } else { + console.log('Symlink created successfully for end to end tests.'); + } + }); }); setup(async () => { @@ -96,6 +114,17 @@ suite('End to End Tests: test adapters', () => { teardown(async () => { pythonTestServer.dispose(); }); + suiteTeardown(async () => { + // remove symlink + const dest = rootPathDiscoverySymlink; + fs.unlink(dest, (err) => { + if (err) { + console.error(err); + } else { + console.log('Symlink removed successfully after tests.'); + } + }); + }); test('unittest discovery adapter small workspace', async () => { // result resolver and saved data for assertions let actualData: { @@ -232,6 +261,90 @@ suite('End to End Tests: test adapters', () => { assert.strictEqual(callCount, 1, 'Expected _resolveDiscovery to be called once'); }); }); + test('pytest discovery adapter small workspace with symlink', async () => { + // result resolver and saved data for assertions + let actualData: { + cwd: string; + tests?: unknown; + status: 'success' | 'error'; + error?: string[]; + }; + // set workspace to test workspace folder + const testSimpleSymlinkPath = path.join(rootPathDiscoverySymlink, 'test_simple.py'); + workspaceUri = Uri.parse(rootPathDiscoverySymlink); + const stats = fs.lstatSync(rootPathDiscoverySymlink); + + // confirm that the path is a symbolic link + assert.ok(stats.isSymbolicLink(), 'The path is not a symbolic link but must be for this test.'); + + resultResolver = new PythonResultResolver(testController, pytestProvider, workspaceUri); + let callCount = 0; + resultResolver._resolveDiscovery = async (payload, _token?) => { + traceLog(`resolveDiscovery ${payload}`); + callCount = callCount + 1; + actualData = payload; + return Promise.resolve(); + }; + // run pytest discovery + const discoveryAdapter = new PytestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel.object, + resultResolver, + envVarsService, + ); + + await discoveryAdapter.discoverTests(workspaceUri, pythonExecFactory).finally(() => { + // verification after discovery is complete + + // 1. Check the status is "success" + assert.strictEqual( + actualData.status, + 'success', + `Expected status to be 'success' instead status is ${actualData.status}`, + ); // 2. Confirm no errors + assert.strictEqual(actualData.error?.length, 0, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.tests, 'Expected tests to be present'); + // 4. Confirm that the cwd returned is the symlink path and the test's path is also using the symlink as the root + if (process.platform === 'win32') { + // covert string to lowercase for windows as the path is case insensitive + traceLog('windows machine detected, converting path to lowercase for comparison'); + const a = actualData.cwd.toLowerCase(); + const b = rootPathDiscoverySymlink.toLowerCase(); + const testSimpleActual = (actualData.tests as { + children: { + path: string; + }[]; + }).children[0].path.toLowerCase(); + const testSimpleExpected = testSimpleSymlinkPath.toLowerCase(); + assert.strictEqual(a, b, `Expected cwd to be the symlink path actual: ${a} expected: ${b}`); + assert.strictEqual( + testSimpleActual, + testSimpleExpected, + `Expected test path to be the symlink path actual: ${testSimpleActual} expected: ${testSimpleExpected}`, + ); + } else { + assert.strictEqual( + path.join(actualData.cwd), + path.join(rootPathDiscoverySymlink), + 'Expected cwd to be the symlink path, check for non-windows machines', + ); + assert.strictEqual( + (actualData.tests as { + children: { + path: string; + }[]; + }).children[0].path, + testSimpleSymlinkPath, + 'Expected test path to be the symlink path, check for non windows machines', + ); + } + + // 5. Confirm that resolveDiscovery was called once + assert.strictEqual(callCount, 1, 'Expected _resolveDiscovery to be called once'); + }); + }); test('pytest discovery adapter large workspace', async () => { // result resolver and saved data for assertions let actualData: { diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts index 7badb5a0350d..45f6d4ffa8eb 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts @@ -6,6 +6,8 @@ import { Uri } from 'vscode'; import * as typeMoq from 'typemoq'; import * as path from 'path'; import { Observable } from 'rxjs/Observable'; +import * as fs from 'fs'; +import * as sinon from 'sinon'; import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; import { ITestServer } from '../../../../client/testing/testController/common/types'; @@ -94,6 +96,9 @@ suite('pytest test discovery adapter', () => { }; }); }); + teardown(() => { + sinon.restore(); + }); test('Discovery should call exec with correct basic args', async () => { // set up exec mock deferred = createDeferred(); @@ -104,6 +109,7 @@ suite('pytest test discovery adapter', () => { deferred.resolve(); return Promise.resolve(execService.object); }); + sinon.stub(fs, 'lstatSync').returns({ isFile: () => true, isSymbolicLink: () => false } as fs.Stats); adapter = new PytestTestDiscoveryAdapter(testServer.object, configService, outputChannel.object); adapter.discoverTests(uri, execFactory.object); // add in await and trigger @@ -143,6 +149,8 @@ suite('pytest test discovery adapter', () => { }), } as unknown) as IConfigurationService; + sinon.stub(fs, 'lstatSync').returns({ isFile: () => true, isSymbolicLink: () => false } as fs.Stats); + // set up exec mock deferred = createDeferred(); execFactory = typeMoq.Mock.ofType(); @@ -161,6 +169,7 @@ suite('pytest test discovery adapter', () => { mockProc.trigger('close'); // verification + const expectedArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only', '.', 'abc', 'xyz']; execService.verify( (x) => @@ -176,4 +185,63 @@ suite('pytest test discovery adapter', () => { typeMoq.Times.once(), ); }); + test('Test discovery adds cwd to pytest args when path is symlink', async () => { + sinon.stub(fs, 'lstatSync').returns({ + isFile: () => true, + isSymbolicLink: () => true, + } as fs.Stats); + + // set up a config service with different pytest args + const configServiceNew: IConfigurationService = ({ + getSettings: () => ({ + testing: { + pytestArgs: ['.', 'abc', 'xyz'], + cwd: expectedPath, + }, + }), + } as unknown) as IConfigurationService; + + // set up exec mock + deferred = createDeferred(); + execFactory = typeMoq.Mock.ofType(); + execFactory + .setup((x) => x.createActivatedEnvironment(typeMoq.It.isAny())) + .returns(() => { + deferred.resolve(); + return Promise.resolve(execService.object); + }); + + adapter = new PytestTestDiscoveryAdapter(testServer.object, configServiceNew, outputChannel.object); + adapter.discoverTests(uri, execFactory.object); + // add in await and trigger + await deferred.promise; + await deferred2.promise; + mockProc.trigger('close'); + + // verification + const expectedArgs = [ + '-m', + 'pytest', + '-p', + 'vscode_pytest', + '--collect-only', + '.', + 'abc', + 'xyz', + `--rootdir=${expectedPath}`, + ]; + execService.verify( + (x) => + x.execObservable( + expectedArgs, + typeMoq.It.is((options) => { + assert.deepEqual(options.env, expectedExtraVariables); + assert.equal(options.cwd, expectedPath); + assert.equal(options.throwOnStdErr, true); + return true; + }), + ), + typeMoq.Times.once(), + ); + }); }); diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index a097df654360..d2ab19368b2d 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -171,7 +171,8 @@ suite('pytest test execution adapter', () => { const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); const pathToPythonScript = path.join(pathToPythonFiles, 'vscode_pytest', 'run_pytest_script.py'); - const expectedArgs = [pathToPythonScript, '--rootdir', myTestPath]; + const rootDirArg = `--rootdir=${myTestPath}`; + const expectedArgs = [pathToPythonScript, rootDirArg]; const expectedExtraVariables = { PYTHONPATH: pathToPythonFiles, TEST_UUID: 'uuid123', @@ -238,7 +239,7 @@ suite('pytest test execution adapter', () => { const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); const pathToPythonScript = path.join(pathToPythonFiles, 'vscode_pytest', 'run_pytest_script.py'); - const expectedArgs = [pathToPythonScript, '--rootdir', myTestPath]; + const expectedArgs = [pathToPythonScript, `--rootdir=${newCwd}`]; const expectedExtraVariables = { PYTHONPATH: pathToPythonFiles, TEST_UUID: 'uuid123', @@ -305,7 +306,7 @@ suite('pytest test execution adapter', () => { x.launchDebugger( typeMoq.It.is((launchOptions) => { assert.equal(launchOptions.cwd, uri.fsPath); - assert.deepEqual(launchOptions.args, ['--rootdir', myTestPath, '--capture', 'no']); + assert.deepEqual(launchOptions.args, [`--rootdir=${myTestPath}`, '--capture=no']); assert.equal(launchOptions.testProvider, 'pytest'); assert.equal(launchOptions.pytestPort, '12345'); assert.equal(launchOptions.pytestUUID, 'uuid123'); diff --git a/src/test/testing/testController/utils.unit.test.ts b/src/test/testing/testController/utils.unit.test.ts index 014261a40232..5bcf8dfa10c8 100644 --- a/src/test/testing/testController/utils.unit.test.ts +++ b/src/test/testing/testController/utils.unit.test.ts @@ -9,6 +9,10 @@ import { ExtractJsonRPCData, parseJsonRPCHeadersAndData, splitTestNameWithRegex, + mapToArgs, + addArgIfNotExist, + argKeyExists, + argsToMap, } from '../../../client/testing/testController/common/utils'; suite('Test Controller Utils: JSON RPC', () => { @@ -158,4 +162,174 @@ ${data}${secondPayload}`; }); }); }); + suite('Test Controller Utils: Args Mapping', () => { + test('Converts map with mixed values to array of strings', async () => { + const inputMap = { + key1: 'value1', + key2: null, + key3: undefined, + key4: 'value4', + }; + const expectedOutput = ['key1=value1', 'key2', 'key4=value4']; + + const result = mapToArgs(inputMap); + + assert.deepStrictEqual(result, expectedOutput); + }); + + test('Returns an empty array for an empty map', async () => { + const inputMap = {}; + const expectedOutput: unknown[] = []; + + const result = mapToArgs(inputMap); + + assert.deepStrictEqual(result, expectedOutput); + }); + + test('Skips undefined values', async () => { + const inputMap = { + key1: undefined, + key2: undefined, + }; + const expectedOutput: unknown[] = []; + + const result = mapToArgs(inputMap); + + assert.deepStrictEqual(result, expectedOutput); + }); + + test('Handles null values correctly', async () => { + const inputMap = { + key1: null, + key2: null, + }; + const expectedOutput = ['key1', 'key2']; + + const result = mapToArgs(inputMap); + + assert.deepStrictEqual(result, expectedOutput); + }); + test('Adds new argument if it does not exist', () => { + const map = {}; + const argKey = 'newKey'; + const argValue = 'newValue'; + + const updatedMap = addArgIfNotExist(map, argKey, argValue); + + assert.deepStrictEqual(updatedMap, { [argKey]: argValue }); + }); + + test('Does not overwrite existing argument', () => { + const map = { existingKey: 'existingValue' }; + const argKey = 'existingKey'; + const argValue = 'newValue'; + + const updatedMap = addArgIfNotExist(map, argKey, argValue); + + assert.deepStrictEqual(updatedMap, { [argKey]: 'existingValue' }); + }); + + test('Handles null value for new key', () => { + const map = {}; + const argKey = 'nullKey'; + const argValue = null; + + const updatedMap = addArgIfNotExist(map, argKey, argValue); + + assert.deepStrictEqual(updatedMap, { [argKey]: argValue }); + }); + + test('Ignores addition if key exists with null value', () => { + const map = { nullKey: null }; + const argKey = 'nullKey'; + const argValue = 'newValue'; + + const updatedMap = addArgIfNotExist(map, argKey, argValue); + + assert.deepStrictEqual(updatedMap, { [argKey]: null }); + }); + + test('Accepts addition if key exists with undefined value', () => { + const map = { undefinedKey: undefined }; + const argKey = 'undefinedKey'; + const argValue = 'newValue'; + + // Attempting to add a key that is explicitly set to undefined + const updatedMap = addArgIfNotExist(map, argKey, argValue); + + // Expect the map to remain unchanged because the key exists as undefined + assert.strictEqual(map[argKey], argValue); + assert.deepStrictEqual(updatedMap, { [argKey]: argValue }); + }); + test('Complex test for argKeyExists with various key types', () => { + const map = { + stringKey: 'stringValue', + nullKey: null, + // Note: not adding an 'undefinedKey' explicitly since it's not present and hence undefined by default + }; + + // Should return true for keys that are present, even with a null value + assert.strictEqual( + argKeyExists(map, 'stringKey'), + true, + "Failed to recognize 'stringKey' which has a string value.", + ); + assert.strictEqual( + argKeyExists(map, 'nullKey'), + true, + "Failed to recognize 'nullKey' which has a null value.", + ); + + // Should return false for keys that are not present + assert.strictEqual( + argKeyExists(map, 'undefinedKey'), + false, + "Incorrectly recognized 'undefinedKey' as existing.", + ); + }); + test('Converts array of strings with "=" into a map', () => { + const args = ['key1=value1', 'key2=value2']; + const expectedMap = { key1: 'value1', key2: 'value2' }; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + + test('Assigns null to keys without "="', () => { + const args = ['key1', 'key2']; + const expectedMap = { key1: null, key2: null }; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + + test('Handles mixed keys with and without "="', () => { + const args = ['key1=value1', 'key2']; + const expectedMap = { key1: 'value1', key2: null }; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + + test('Handles strings with multiple "=" characters', () => { + const args = ['key1=part1=part2']; + const expectedMap = { key1: 'part1=part2' }; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + + test('Returns an empty map for an empty input array', () => { + const args: ReadonlyArray = []; + const expectedMap = {}; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + }); }); From bae7d406852335732383f55cb8dd2ad31dd951a6 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:38:10 -0800 Subject: [PATCH 023/767] Update vscode-tas-client version (#22876) --- package-lock.json | 92 +++++++++-------------------------------------- package.json | 2 +- 2 files changed, 17 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62105cb4fabb..172505674beb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "vscode-jsonrpc": "^8.2.0", "vscode-languageclient": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", - "vscode-tas-client": "^0.1.75", + "vscode-tas-client": "^0.1.84", "which": "^2.0.2", "winreg": "^1.2.4", "xml2js": "^0.5.0" @@ -2861,16 +2861,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -6732,25 +6722,6 @@ "readable-stream": "^2.3.6" } }, - "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -11532,11 +11503,6 @@ "react-is": "^16.13.1" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -13346,12 +13312,9 @@ } }, "node_modules/tas-client": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.1.73.tgz", - "integrity": "sha512-UDdUF9kV2hYdlv+7AgqP2kXarVSUhjK7tg1BUflIRGEgND0/QoNpN64rcEuhEcM8AIbW65yrCopJWqRhLZ3m8w==", - "dependencies": { - "axios": "^1.6.1" - } + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", + "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, "node_modules/terser": { "version": "5.14.2", @@ -14562,14 +14525,14 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-tas-client": { - "version": "0.1.75", - "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.75.tgz", - "integrity": "sha512-/+ALFWPI4U3obeRvLFSt39guT7P9bZQrkmcLoiS+2HtzJ/7iPKNt5Sj+XTiitGlPYVFGFc0plxX8AAp6Uxs0xQ==", + "version": "0.1.84", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz", + "integrity": "sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w==", "dependencies": { - "tas-client": "0.1.73" + "tas-client": "0.2.33" }, "engines": { - "vscode": "^1.19.1" + "vscode": "^1.85.0" } }, "node_modules/vscode-uri": { @@ -17538,16 +17501,6 @@ "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==", "dev": true }, - "axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -20624,11 +20577,6 @@ "readable-stream": "^2.3.6" } }, - "follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -24347,11 +24295,6 @@ "react-is": "^16.13.1" } }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -25765,12 +25708,9 @@ } }, "tas-client": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.1.73.tgz", - "integrity": "sha512-UDdUF9kV2hYdlv+7AgqP2kXarVSUhjK7tg1BUflIRGEgND0/QoNpN64rcEuhEcM8AIbW65yrCopJWqRhLZ3m8w==", - "requires": { - "axios": "^1.6.1" - } + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", + "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, "terser": { "version": "5.14.2", @@ -26705,11 +26645,11 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "vscode-tas-client": { - "version": "0.1.75", - "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.75.tgz", - "integrity": "sha512-/+ALFWPI4U3obeRvLFSt39guT7P9bZQrkmcLoiS+2HtzJ/7iPKNt5Sj+XTiitGlPYVFGFc0plxX8AAp6Uxs0xQ==", + "version": "0.1.84", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz", + "integrity": "sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w==", "requires": { - "tas-client": "0.1.73" + "tas-client": "0.2.33" } }, "vscode-uri": { diff --git a/package.json b/package.json index c211df36a0d6..efe8cba4f5d6 100644 --- a/package.json +++ b/package.json @@ -1569,7 +1569,7 @@ "vscode-jsonrpc": "^8.2.0", "vscode-languageclient": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", - "vscode-tas-client": "^0.1.75", + "vscode-tas-client": "^0.1.84", "which": "^2.0.2", "winreg": "^1.2.4", "xml2js": "^0.5.0" From 178a0b2ee9010fd154cad8cccf8550daf9a54a99 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Wed, 21 Feb 2024 09:11:16 -0800 Subject: [PATCH 024/767] Fix bug with symlink for pytest execution (#22952) Fixes https://github.com/microsoft/vscode-python/issues/22938 --- .../expected_execution_test_output.py | 12 ++++ .../tests/pytestadapter/test_execution.py | 56 ++++++++++++++++++- pythonFiles/vscode_pytest/__init__.py | 32 ++++++----- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py index 44f3d3d0abce..db4e493c3daa 100644 --- a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py @@ -684,3 +684,15 @@ "subtest": None, }, } + +# Constant for the symlink execution test where TEST_DATA_PATH / "root" the target and TEST_DATA_PATH / "symlink_folder" the symlink +test_a_symlink_path = TEST_DATA_PATH / "symlink_folder" / "tests" / "test_a.py" +symlink_run_expected_execution_output = { + get_absolute_test_id("test_a.py::test_a_function", test_a_symlink_path): { + "test": get_absolute_test_id("test_a.py::test_a_function", test_a_symlink_path), + "outcome": "success", + "message": None, + "traceback": None, + "subtest": None, + } +} diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index dd32b61fa262..1defe2f52b82 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import os import shutil from typing import Any, Dict, List @@ -8,7 +9,13 @@ from tests.pytestadapter import expected_execution_test_output -from .helpers import TEST_DATA_PATH, runner, runner_with_cwd +from .helpers import ( + TEST_DATA_PATH, + create_symlink, + get_absolute_test_id, + runner, + runner_with_cwd, +) def test_config_file(): @@ -276,3 +283,50 @@ def test_pytest_execution(test_ids, expected_const): if actual_result_dict[key]["traceback"] is not None: actual_result_dict[key]["traceback"] = "TRACEBACK" assert actual_result_dict == expected_const + + +def test_symlink_run(): + """ + Test to test pytest discovery with the command line arg --rootdir specified as a symlink path. + Discovery should succeed and testids should be relative to the symlinked root directory. + """ + with create_symlink(TEST_DATA_PATH, "root", "symlink_folder") as ( + source, + destination, + ): + assert destination.is_symlink() + test_a_path = TEST_DATA_PATH / "symlink_folder" / "tests" / "test_a.py" + test_a_id = get_absolute_test_id( + "tests/test_a.py::test_a_function", + test_a_path, + ) + + # Run pytest with the cwd being the resolved symlink path (as it will be when we run the subprocess from node). + actual = runner_with_cwd( + [f"--rootdir={os.fspath(destination)}", test_a_id], source + ) + + expected_const = ( + expected_execution_test_output.symlink_run_expected_execution_output + ) + assert actual + actual_list: List[Dict[str, Any]] = actual + if actual_list is not None: + assert actual_list.pop(-1).get("eot") + actual_item = actual_list.pop(0) + try: + # Check if all requirements + assert all( + item in actual_item.keys() for item in ("status", "cwd", "result") + ), "Required keys are missing" + assert actual_item.get("status") == "success", "Status is not 'success'" + assert actual_item.get("cwd") == os.fspath( + destination + ), f"CWD does not match: {os.fspath(destination)}" + actual_result_dict = dict() + actual_result_dict.update(actual_item["result"]) + assert actual_result_dict == expected_const + except AssertionError as e: + # Print the actual_item in JSON format if an assertion fails + print(json.dumps(actual_item, indent=4)) + pytest.fail(str(e)) diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index 256f2bfdb099..bdeefde469f4 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -225,6 +225,8 @@ def pytest_report_teststatus(report, config): config -- configuration object. """ cwd = pathlib.Path.cwd() + if SYMLINK_PATH: + cwd = SYMLINK_PATH if report.when == "call": traceback = None @@ -348,10 +350,7 @@ def pytest_sessionfinish(session, exitstatus): cwd = pathlib.Path.cwd() if SYMLINK_PATH: print("Plugin warning[vscode-pytest]: SYMLINK set, adjusting cwd.") - # Get relative between the cwd (resolved path) and the node path. - rel_path = os.path.relpath(cwd, pathlib.Path.cwd()) - # Calculate the new node path by making it relative to the symlink path. - cwd = pathlib.Path(os.path.join(SYMLINK_PATH, rel_path)) + cwd = pathlib.Path(SYMLINK_PATH) if IS_DISCOVERY: if not (exitstatus == 0 or exitstatus == 1 or exitstatus == 5): @@ -681,9 +680,9 @@ def get_node_path(node: Any) -> pathlib.Path: A function that returns the path of a node given the switch to pathlib.Path. It also evaluates if the node is a symlink and returns the equivalent path. """ - path = getattr(node, "path", None) or pathlib.Path(node.fspath) + node_path = getattr(node, "path", None) or pathlib.Path(node.fspath) - if not path: + if not node_path: raise VSCodePytestError( f"Unable to find path for node: {node}, node.path: {node.path}, node.fspath: {node.fspath}" ) @@ -692,17 +691,24 @@ def get_node_path(node: Any) -> pathlib.Path: if SYMLINK_PATH and not isinstance(node, pytest.Session): # Get relative between the cwd (resolved path) and the node path. try: - rel_path = path.relative_to(pathlib.Path.cwd()) - - # Calculate the new node path by making it relative to the symlink path. - sym_path = pathlib.Path(os.path.join(SYMLINK_PATH, rel_path)) - return sym_path + # check to see if the node path contains the symlink root already + common_path = os.path.commonpath([SYMLINK_PATH, node_path]) + if common_path == os.fsdecode(SYMLINK_PATH): + # node path is already relative to the SYMLINK_PATH root therefore return + return node_path + else: + # if the node path is not a symlink, then we need to calculate the equivalent symlink path + # get the relative path between the cwd and the node path (as the node path is not a symlink) + rel_path = node_path.relative_to(pathlib.Path.cwd()) + # combine the difference between the cwd and the node path with the symlink path + sym_path = pathlib.Path(os.path.join(SYMLINK_PATH, rel_path)) + return sym_path except Exception as e: raise VSCodePytestError( f"Error occurred while calculating symlink equivalent from node path: {e}" - "\n SYMLINK_PATH: {SYMLINK_PATH}, \n node path: {path}, \n cwd: {{pathlib.Path.cwd()}}" + f"\n SYMLINK_PATH: {SYMLINK_PATH}, \n node path: {node_path}, \n cwd: {pathlib.Path.cwd()}" ) - return path + return node_path __socket = None From ba945536ef25b42a8ad342f91097463c7b1fd944 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Wed, 21 Feb 2024 23:34:11 +0530 Subject: [PATCH 025/767] Modify telemetry to contain trigger time as property (#22941) --- src/client/common/persistentState.ts | 2 +- src/client/extension.ts | 6 ++++-- src/client/languageServer/watcher.ts | 4 +++- src/client/startupTelemetry.ts | 11 ++++++++--- src/client/telemetry/constants.ts | 2 +- src/client/telemetry/index.ts | 17 ++++++++++++++--- 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/client/common/persistentState.ts b/src/client/common/persistentState.ts index 2959a2dc8216..ea86b2e145ba 100644 --- a/src/client/common/persistentState.ts +++ b/src/client/common/persistentState.ts @@ -108,7 +108,7 @@ export class PersistentState implements IPersistentState { export const GLOBAL_PERSISTENT_KEYS_DEPRECATED = 'PYTHON_EXTENSION_GLOBAL_STORAGE_KEYS'; export const WORKSPACE_PERSISTENT_KEYS_DEPRECATED = 'PYTHON_EXTENSION_WORKSPACE_STORAGE_KEYS'; -const GLOBAL_PERSISTENT_KEYS = 'PYTHON_GLOBAL_STORAGE_KEYS'; +export const GLOBAL_PERSISTENT_KEYS = 'PYTHON_GLOBAL_STORAGE_KEYS'; const WORKSPACE_PERSISTENT_KEYS = 'PYTHON_WORKSPACE_STORAGE_KEYS'; type KeysStorageType = 'global' | 'workspace'; export type KeysStorage = { key: string; defaultValue: unknown }; diff --git a/src/client/extension.ts b/src/client/extension.ts index a4c31d91eb3b..b9f32187413b 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -46,6 +46,7 @@ import { WorkspaceService } from './common/application/workspace'; import { disposeAll } from './common/utils/resourceLifecycle'; import { ProposedExtensionAPI } from './proposedApiTypes'; import { buildProposedApi } from './proposedApi'; +import { GLOBAL_PERSISTENT_KEYS } from './common/persistentState'; durations.codeLoadingTime = stopWatch.elapsedTime; @@ -62,7 +63,9 @@ export async function activate(context: IExtensionContext): Promise; let serviceContainer: IServiceContainer; + let isFirstSession: boolean | undefined; try { + isFirstSession = context.globalState.get(GLOBAL_PERSISTENT_KEYS, []).length === 0; const workspaceService = new WorkspaceService(); context.subscriptions.push( workspaceService.onDidGrantWorkspaceTrust(async () => { @@ -79,8 +82,7 @@ export async function activate(context: IExtensionContext): Promise { +async function getActivationTelemetryProps( + serviceContainer: IServiceContainer, + isFirstSession?: boolean, +): Promise { // TODO: Not all of this data is showing up in the database... // TODO: If any one of these parts fails we send no info. We should @@ -88,7 +92,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const terminalHelper = serviceContainer.get(ITerminalHelper); const terminalShellType = terminalHelper.identifyTerminalShell(); if (!workspaceService.isTrusted) { - return { workspaceFolderCount, terminal: terminalShellType }; + return { workspaceFolderCount, terminal: terminalShellType, isFirstSession }; } const interpreterService = serviceContainer.get(IInterpreterService); const mainWorkspaceUri = workspaceService.workspaceFolders?.length @@ -132,5 +136,6 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): usingUserDefinedInterpreter, usingGlobalInterpreter, appName, + isFirstSession, }; } diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index 3321653c3bd7..b405143c0ba3 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -64,7 +64,7 @@ export enum EventName { EXTENSION_SURVEY_PROMPT = 'EXTENSION_SURVEY_PROMPT', LANGUAGE_SERVER_ENABLED = 'LANGUAGE_SERVER.ENABLED', - LANGUAGE_SERVER_TRIGGER_DURATION = 'LANGUAGE_SERVER.TRIGGER_DURATION', + LANGUAGE_SERVER_TRIGGER_TIME = 'LANGUAGE_SERVER_TRIGGER_TIME', LANGUAGE_SERVER_STARTUP = 'LANGUAGE_SERVER.STARTUP', LANGUAGE_SERVER_READY = 'LANGUAGE_SERVER.READY', LANGUAGE_SERVER_TELEMETRY = 'LANGUAGE_SERVER.EVENT', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index b2ed37939fa9..bb2cd6d77896 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -723,6 +723,11 @@ export interface IEventNamePropertyMapping { * If global interpreter is being used */ usingGlobalInterpreter?: boolean; + /** + * Carries `true` if it is the very first session of the user. We check whether persistent cache is empty + * to approximately guess if it's the first session. + */ + isFirstSession?: boolean; }; /** * Telemetry event sent when substituting Environment variables to calculate value of variables @@ -1329,11 +1334,17 @@ export interface IEventNamePropertyMapping { * Track how long it takes to trigger language server activation code, after Python extension starts activating. */ /* __GDPR__ - "language_server_trigger_duration" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karrtikr", "isMeasurement": true } + "language_server_trigger_time" : { + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "karrtikr" }, + "triggerTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "karrtikr" } } */ - [EventName.LANGUAGE_SERVER_TRIGGER_DURATION]: unknown; + [EventName.LANGUAGE_SERVER_TRIGGER_TIME]: { + /** + * Time it took to trigger language server startup. + */ + triggerTime: number; + }; /** * Telemetry event sent when starting Node.js server */ From 4fca030f95ce99f29d616360bbf7ee4cf5fad37e Mon Sep 17 00:00:00 2001 From: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:32:58 -0600 Subject: [PATCH 026/767] Adding GDPR tag for isFirstSession (#22955) --- src/client/telemetry/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index bb2cd6d77896..bdb098be520e 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -681,7 +681,8 @@ export interface IEventNamePropertyMapping { "totalactivatetime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" }, "totalnonblockingactivatetime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" }, "usinguserdefinedinterpreter" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" }, - "usingglobalinterpreter" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" } + "usingglobalinterpreter" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" }, + "isfirstsession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "luabud" } } */ [EventName.EDITOR_LOAD]: { From fa7533760995fb2110128c06ebae0c46312108ba Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 26 Feb 2024 13:51:29 +0530 Subject: [PATCH 027/767] Fix duplicated environments in interpreter list (#22964) Failing tests are due to https://github.com/microsoft/vscode-python/issues/22965, this can still be reviewed. Closes https://github.com/microsoft/vscode-python/issues/22918 closes https://github.com/microsoft/vscode-python/issues/22146 --- src/client/pythonEnvironments/base/locator.ts | 4 +- .../pythonEnvironments/base/locatorUtils.ts | 2 - .../locators/common/resourceBasedLocator.ts | 13 +--- .../composite/envsCollectionService.ts | 8 +-- .../base/locators/composite/envsReducer.ts | 23 ++++--- .../base/locators/composite/envsResolver.ts | 12 ++-- .../lowLevel/windowsRegistryLocator.ts | 69 +++++++------------ .../windowsRegistryLocator.testvirtualenvs.ts | 9 ++- .../windowsRegistryLocator.unit.test.ts | 25 +++++-- 9 files changed, 74 insertions(+), 91 deletions(-) diff --git a/src/client/pythonEnvironments/base/locator.ts b/src/client/pythonEnvironments/base/locator.ts index 3fd5194c37da..eca6d0ed1ba6 100644 --- a/src/client/pythonEnvironments/base/locator.ts +++ b/src/client/pythonEnvironments/base/locator.ts @@ -20,7 +20,7 @@ export type PythonEnvUpdatedEvent = { /** * The iteration index of The env info that was previously provided. */ - index?: number; + index: number; /** * The env info that was previously provided. */ @@ -243,7 +243,7 @@ export interface IDiscoveryAPI { resolveEnv(path: string): Promise; } -interface IEmitter { +export interface IEmitter { fire(e: E): void; } diff --git a/src/client/pythonEnvironments/base/locatorUtils.ts b/src/client/pythonEnvironments/base/locatorUtils.ts index faeaa84bedf4..6af8c0ee1b69 100644 --- a/src/client/pythonEnvironments/base/locatorUtils.ts +++ b/src/client/pythonEnvironments/base/locatorUtils.ts @@ -95,8 +95,6 @@ export async function getEnvs(iterator: IPythonEnvsIterator imp await this.disposables.dispose(); } - public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { - const iterator = this.doIterEnvs(query); - const it = this._iterEnvs(iterator, query); - it.onUpdated = iterator.onUpdated; - return it; - } - - private async *_iterEnvs( - iterator: IPythonEnvsIterator, - query?: PythonLocatorQuery, - ): IPythonEnvsIterator { + public async *iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator { await this.activate(); + const iterator = this.doIterEnvs(query); if (query?.envPath) { let result = await iterator.next(); while (!result.done) { diff --git a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts index bcc9877ad14a..a54489e34633 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts @@ -141,12 +141,12 @@ export class EnvsCollectionService extends PythonEnvsWatcher(); if (iterator.onUpdated !== undefined) { - iterator.onUpdated(async (event) => { + const listener = iterator.onUpdated(async (event) => { if (isProgressEvent(event)) { switch (event.stage) { case ProgressReportStage.discoveryFinished: state.done = true; - // listener.dispose(); + listener.dispose(); break; case ProgressReportStage.allPathsDiscovered: if (!query) { @@ -164,10 +164,6 @@ export class EnvsCollectionService extends PythonEnvsWatcher { + const listener = iterator.onUpdated((event) => { if (isProgressEvent(event)) { if (event.stage === ProgressReportStage.discoveryFinished) { state.done = true; - // For super slow locators such as Windows registry, we expect updates even after discovery - // is "officially" finished, hence do not dispose listeners. - // listener.dispose(); + listener.dispose(); } else { didUpdate.fire(event); } @@ -69,11 +66,15 @@ async function* iterEnvsIterator( const oldEnv = seen[event.index]; seen[event.index] = event.update; didUpdate.fire({ index: event.index, old: oldEnv, update: event.update }); - } else if (event.update) { - didUpdate.fire({ update: event.update }); + } else { + // This implies a problem in a downstream locator + traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`); } + state.pending -= 1; checkIfFinishedAndNotify(state, didUpdate); }); + } else { + didUpdate.fire({ stage: ProgressReportStage.discoveryStarted }); } let result = await iterator.next(); @@ -89,8 +90,10 @@ async function* iterEnvsIterator( } result = await iterator.next(); } - state.done = true; - checkIfFinishedAndNotify(state, didUpdate); + if (iterator.onUpdated === undefined) { + state.done = true; + checkIfFinishedAndNotify(state, didUpdate); + } } async function resolveDifferencesInBackground( @@ -124,8 +127,8 @@ function checkIfFinishedAndNotify( ) { if (state.done && state.pending === 0) { didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); + didUpdate.dispose(); traceVerbose(`Finished with environment reducer`); - state.done = false; // No need to notify again. } } diff --git a/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts b/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts index 752f5778c73c..fa998557deb4 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsResolver.ts @@ -81,15 +81,13 @@ export class PythonEnvsResolver implements IResolvingLocator { const seen: PythonEnvInfo[] = []; if (iterator.onUpdated !== undefined) { - iterator.onUpdated(async (event) => { + const listener = iterator.onUpdated(async (event) => { state.pending += 1; if (isProgressEvent(event)) { if (event.stage === ProgressReportStage.discoveryFinished) { didUpdate.fire({ stage: ProgressReportStage.allPathsDiscovered }); state.done = true; - // For super slow locators such as Windows registry, we expect updates even after discovery - // is "officially" finished, hence do not dispose listeners. - // listener.dispose(); + listener.dispose(); } else { didUpdate.fire(event); } @@ -97,14 +95,15 @@ export class PythonEnvsResolver implements IResolvingLocator { throw new Error( 'Unsupported behavior: `undefined` environment updates are not supported from downstream locators in resolver', ); - } else if (event.index && seen[event.index] !== undefined) { + } else if (event.index !== undefined && seen[event.index] !== undefined) { const old = seen[event.index]; await setKind(event.update, environmentKinds); seen[event.index] = await resolveBasicEnv(event.update); didUpdate.fire({ old, index: event.index, update: seen[event.index] }); this.resolveInBackground(event.index, state, didUpdate, seen).ignoreErrors(); } else { - didUpdate.fire({ update: await this.resolveEnv(event.update.executablePath) }); + // This implies a problem in a downstream locator + traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`); } state.pending -= 1; checkIfFinishedAndNotify(state, didUpdate); @@ -174,6 +173,7 @@ function checkIfFinishedAndNotify( ) { if (state.done && state.pending === 0) { didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); + didUpdate.dispose(); traceVerbose(`Finished with environment resolver`); } } diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts index c6ba64cdb469..4f5a14721a19 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts @@ -3,76 +3,53 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { EventEmitter } from 'vscode'; import { PythonEnvKind, PythonEnvSource } from '../../info'; -import { - BasicEnvInfo, - IPythonEnvsIterator, - Locator, - ProgressNotificationEvent, - ProgressReportStage, - PythonEnvUpdatedEvent, -} from '../../locator'; +import { BasicEnvInfo, IPythonEnvsIterator, Locator, PythonLocatorQuery, IEmitter } from '../../locator'; import { getRegistryInterpreters } from '../../../common/windowsUtils'; import { traceError, traceVerbose } from '../../../../logging'; import { isMicrosoftStoreDir } from '../../../common/environmentManagers/microsoftStoreEnv'; import { inExperiment } from '../../../common/externalDependencies'; import { DiscoveryUsingWorkers } from '../../../../common/experiments/groups'; +import { PythonEnvsChangedEvent } from '../../watcher'; + +export const WINDOWS_REG_PROVIDER_ID = 'windows-registry'; export class WindowsRegistryLocator extends Locator { - public readonly providerId: string = 'windows-registry'; + public readonly providerId: string = WINDOWS_REG_PROVIDER_ID; // eslint-disable-next-line class-methods-use-this public iterEnvs( - _?: unknown, + query?: PythonLocatorQuery, useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment), ): IPythonEnvsIterator { - const didUpdate = new EventEmitter | ProgressNotificationEvent>(); - const iterator = useWorkerThreads ? iterEnvsIterator(didUpdate) : oldIterEnvsIterator(); if (useWorkerThreads) { - iterator.onUpdated = didUpdate.event; + /** + * Windows registry is slow and often not necessary, so notify completion immediately, but use watcher + * change events to signal for any new envs which are found. + */ + if (query?.providerId === this.providerId) { + // Query via change event, so iterate all envs. + return iterateEnvs(true); + } + return iterateEnvsLazily(this.emitter); } - return iterator; + return iterateEnvs(false); } } -/** - * Windows registry is slow and often not necessary, so notify completion immediately, while still updating lazily as we find stuff. - * To accomplish this, use an empty iterator while lazily firing environments using updates. - */ -async function* iterEnvsIterator( - didUpdate: EventEmitter | ProgressNotificationEvent>, -): IPythonEnvsIterator { - updateLazily(didUpdate).ignoreErrors(); +async function* iterateEnvsLazily(changed: IEmitter): IPythonEnvsIterator { + loadAllEnvs(changed).ignoreErrors(); } -async function updateLazily(didUpdate: EventEmitter | ProgressNotificationEvent>) { +async function loadAllEnvs(changed: IEmitter) { traceVerbose('Searching for windows registry interpreters'); - const interpreters = await getRegistryInterpreters(true); - for (const interpreter of interpreters) { - try { - // Filter out Microsoft Store app directories. We have a store app locator that handles this. - // The python.exe available in these directories might not be python. It can be a store install - // shortcut that takes you to microsoft store. - if (isMicrosoftStoreDir(interpreter.interpreterPath)) { - continue; - } - const env: BasicEnvInfo = { - kind: PythonEnvKind.OtherGlobal, - executablePath: interpreter.interpreterPath, - source: [PythonEnvSource.WindowsRegistry], - }; - didUpdate.fire({ update: env }); - } catch (ex) { - traceError(`Failed to process environment: ${interpreter}`, ex); - } - } - didUpdate.fire({ stage: ProgressReportStage.discoveryFinished }); + await getRegistryInterpreters(true); + changed.fire({ providerId: WINDOWS_REG_PROVIDER_ID }); traceVerbose('Finished searching for windows registry interpreters'); } -async function* oldIterEnvsIterator(): IPythonEnvsIterator { - const interpreters = await getRegistryInterpreters(false); +async function* iterateEnvs(useWorkerThreads: boolean): IPythonEnvsIterator { + const interpreters = await getRegistryInterpreters(useWorkerThreads); for (const interpreter of interpreters) { try { // Filter out Microsoft Store app directories. We have a store app locator that handles this. diff --git a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts index 693d7c1b7fee..110464356040 100644 --- a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts +++ b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts @@ -2,7 +2,10 @@ // Licensed under the MIT License. import { getEnvs } from '../../../../../client/pythonEnvironments/base/locatorUtils'; -import { WindowsRegistryLocator } from '../../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; +import { + WindowsRegistryLocator, + WINDOWS_REG_PROVIDER_ID, +} from '../../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; import { assertBasicEnvsEqual } from '../envTestUtils'; import { TEST_TIMEOUT } from '../../../../constants'; import { getOSType, OSType } from '../../../../../client/common/utils/platform'; @@ -19,8 +22,8 @@ suite('Windows Registry Locator', async () => { }); test('Worker thread to fetch registry interpreters is working', async () => { - const items = await getEnvs(locator.iterEnvs(undefined, false)); - const workerItems = await getEnvs(locator.iterEnvs(undefined, true)); + const items = await getEnvs(locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, false)); + const workerItems = await getEnvs(locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, true)); console.log('Number of items Windows registry locator returned:', items.length); // Make sure items returned when using worker threads v/s not are the same. assertBasicEnvsEqual(items, workerItems); diff --git a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts index 4197c36fa9f7..07a7a864ef74 100644 --- a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts +++ b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.unit.test.ts @@ -4,10 +4,14 @@ import * as assert from 'assert'; import * as path from 'path'; import * as sinon from 'sinon'; +import { expect } from 'chai'; import { PythonEnvKind, PythonEnvSource } from '../../../../../client/pythonEnvironments/base/info'; import { getEnvs } from '../../../../../client/pythonEnvironments/base/locatorUtils'; import * as winreg from '../../../../../client/pythonEnvironments/common/windowsRegistry'; -import { WindowsRegistryLocator } from '../../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; +import { + WindowsRegistryLocator, + WINDOWS_REG_PROVIDER_ID, +} from '../../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; import { createBasicEnv } from '../../common'; import { TEST_LAYOUT_ROOT } from '../../../common/commonTestConstants'; import { assertBasicEnvsEqual } from '../envTestUtils'; @@ -201,7 +205,7 @@ suite('Windows Registry', () => { } setup(async () => { - sinon.stub(externalDependencies, 'inExperiment').returns(false); + sinon.stub(externalDependencies, 'inExperiment').returns(true); stubReadRegistryValues = sinon.stub(winreg, 'readRegistryValues'); stubReadRegistryKeys = sinon.stub(winreg, 'readRegistryKeys'); stubReadRegistryValues.callsFake(fakeRegistryValues); @@ -222,18 +226,29 @@ suite('Windows Registry', () => { createBasicEnv(PythonEnvKind.OtherGlobal, path.join(regTestRoot, 'python38', 'python.exe')), ].map((e) => ({ ...e, source: [PythonEnvSource.WindowsRegistry] })); - const iterator = locator.iterEnvs(undefined, true); + const lazyIterator = locator.iterEnvs(undefined, true); + const envs = await getEnvs(lazyIterator); + expect(envs.length).to.equal(0); + + const iterator = locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, true); const actualEnvs = await getEnvs(iterator); assertBasicEnvsEqual(actualEnvs, expectedEnvs); }); + test('iterEnvs(): query is undefined', async () => { + // Iterate no envs when query is `undefined`, i.e notify completion immediately. + const lazyIterator = locator.iterEnvs(undefined, true); + const envs = await getEnvs(lazyIterator); + expect(envs.length).to.equal(0); + }); + test('iterEnvs(): no registry permission', async () => { stubReadRegistryKeys.callsFake(() => { throw Error(); }); - const iterator = locator.iterEnvs(undefined, true); + const iterator = locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, true); const actualEnvs = await getEnvs(iterator); assert.deepStrictEqual(actualEnvs, []); @@ -252,7 +267,7 @@ suite('Windows Registry', () => { createBasicEnv(PythonEnvKind.OtherGlobal, path.join(regTestRoot, 'python38', 'python.exe')), ].map((e) => ({ ...e, source: [PythonEnvSource.WindowsRegistry] })); - const iterator = locator.iterEnvs(undefined, true); + const iterator = locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, true); const actualEnvs = await getEnvs(iterator); assertBasicEnvsEqual(actualEnvs, expectedEnvs); From e6c859dc03b32a4b5f6bc83a8fa4df2798734c19 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 26 Feb 2024 17:02:39 +0530 Subject: [PATCH 028/767] Add more validation checks when accessing envs from cache (#22966) Failing tests are not related to the PR, so this is ready for review. For https://github.com/microsoft/vscode-python/issues/22877 --- .../base/locators/composite/envsCollectionCache.ts | 4 ++++ .../locators/composite/envsCollectionService.unit.test.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts b/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts index dadab2512e16..456e8adfa9a4 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts @@ -245,6 +245,10 @@ export class PythonEnvInfoCache extends PythonEnvsWatcher { ) { const env = buildEnvInfo({ executable, searchLocation, name, location, kind }); env.id = id ?? env.id; + env.version.major = 3; + env.version.minor = 10; + env.version.micro = 10; return env; } From 7e350603adfdd6caf50d7d37a09e830a59b09a92 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 26 Feb 2024 22:48:26 +0530 Subject: [PATCH 029/767] Reactivate terminals if `.env` file changes (#22969) Closes https://github.com/microsoft/vscode-python/issues/22920 --- .../terminals/envCollectionActivation/service.ts | 11 ++++++++++- .../terminalEnvVarCollectionService.unit.test.ts | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index aaa4ccf23bea..3ca0b39582ae 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -32,7 +32,7 @@ import { defaultShells } from '../../interpreter/activation/service'; import { IEnvironmentActivationService } from '../../interpreter/activation/types'; import { EnvironmentType, PythonEnvironment } from '../../pythonEnvironments/info'; import { getSearchPathEnvVarNames } from '../../common/utils/exec'; -import { EnvironmentVariables } from '../../common/variables/types'; +import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../common/variables/types'; import { TerminalShellType } from '../../common/terminal/types'; import { OSType } from '../../common/utils/platform'; import { normCase } from '../../common/platform/fs-paths'; @@ -81,6 +81,8 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ @inject(ITerminalDeactivateService) private readonly terminalDeactivateService: ITerminalDeactivateService, @inject(IPathUtils) private readonly pathUtils: IPathUtils, @inject(IShellIntegrationService) private readonly shellIntegrationService: IShellIntegrationService, + @inject(IEnvironmentVariablesProvider) + private readonly environmentVariablesProvider: IEnvironmentVariablesProvider, ) { this.separator = platform.osType === OSType.Windows ? ';' : ':'; this.progressService = new ProgressService(this.shell); @@ -119,6 +121,13 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ this, this.disposables, ); + this.environmentVariablesProvider.onDidEnvironmentVariablesChange( + async (r: Resource) => { + await this._applyCollection(r).ignoreErrors(); + }, + this, + this.disposables, + ); this.applicationEnvironment.onDidChangeShell( async (shell: string) => { this.processEnvVars = undefined; diff --git a/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts b/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts index 251c0d1aabaf..22b5dcf7477a 100644 --- a/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts +++ b/src/test/interpreters/activation/terminalEnvVarCollectionService.unit.test.ts @@ -38,6 +38,7 @@ import { PathUtils } from '../../../client/common/platform/pathUtils'; import { PythonEnvType } from '../../../client/pythonEnvironments/base/info'; import { PythonEnvironment } from '../../../client/pythonEnvironments/info'; import { IShellIntegrationService, ITerminalDeactivateService } from '../../../client/terminals/types'; +import { IEnvironmentVariablesProvider } from '../../../client/common/variables/types'; suite('Terminal Environment Variable Collection Service', () => { let platform: IPlatformService; @@ -74,6 +75,7 @@ suite('Terminal Environment Variable Collection Service', () => { interpreterService = mock(); context = mock(); shell = mock(); + const envVarProvider = mock(); shellIntegrationService = mock(); when(shellIntegrationService.isWorking()).thenResolve(true); globalCollection = mock(); @@ -113,6 +115,7 @@ suite('Terminal Environment Variable Collection Service', () => { instance(terminalDeactivateService), new PathUtils(getOSType() === OSType.Windows), instance(shellIntegrationService), + instance(envVarProvider), ); }); From 39e2bee9d926ab0bb0780d44f98f659f2710de65 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 26 Feb 2024 13:46:21 -0800 Subject: [PATCH 030/767] skip tests, pytest upstream regression (#22974) Short-term fix to stop CI from failing due to a regression upstream from pytest. See issue for details: https://github.com/microsoft/vscode-python/issues/22965. --- pythonFiles/tests/pytestadapter/test_discovery.py | 8 ++++++++ pythonFiles/tests/pytestadapter/test_execution.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index b28a2b307ae2..a1f4e4f266ae 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -18,6 +18,10 @@ from .helpers import TEST_DATA_PATH, runner, runner_with_cwd, create_symlink +@pytest.mark.skipif( + sys.platform == "win32", + reason="See https://github.com/microsoft/vscode-python/issues/22965", +) def test_import_error(tmp_path): """Test pytest discovery on a file that has a pytest marker but does not import pytest. @@ -59,6 +63,10 @@ def test_import_error(tmp_path): assert False +@pytest.mark.skipif( + sys.platform == "win32", + reason="See https://github.com/microsoft/vscode-python/issues/22965", +) def test_syntax_error(tmp_path): """Test pytest discovery on a file that has a syntax error. diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 1defe2f52b82..a8336089d0a9 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List import pytest +import sys from tests.pytestadapter import expected_execution_test_output @@ -71,6 +72,10 @@ def test_rootdir_specified(): assert actual_result_dict == expected_const +@pytest.mark.skipif( + sys.platform == "win32", + reason="See https://github.com/microsoft/vscode-python/issues/22965", +) def test_syntax_error_execution(tmp_path): """Test pytest execution on a file that has a syntax error. From ade2c37d42bc50a1aee29968ece552c5a6414726 Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:18:02 -0800 Subject: [PATCH 031/767] Bump release 2024.2 (#22972) Bump release 2024.2 --- package-lock.json | 4 ++-- package.json | 2 +- pythonFiles/install_debugpy.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 172505674beb..871489b89077 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "python", - "version": "2024.1.0-dev", + "version": "2024.2.0-rc", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "python", - "version": "2024.1.0-dev", + "version": "2024.2.0-rc", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index efe8cba4f5d6..02339c7e00f2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "python", "displayName": "Python", "description": "IntelliSense (Pylance), Linting, Debugging (Python Debugger), code formatting, refactoring, unit tests, and more.", - "version": "2024.1.0-dev", + "version": "2024.2.0-rc", "featureFlags": { "usingNewInterpreterStorage": true }, diff --git a/pythonFiles/install_debugpy.py b/pythonFiles/install_debugpy.py index 9377d00237d7..c217e81fd219 100644 --- a/pythonFiles/install_debugpy.py +++ b/pythonFiles/install_debugpy.py @@ -13,7 +13,7 @@ DEBUGGER_DEST = os.path.join(EXTENSION_ROOT, "pythonFiles", "lib", "python") DEBUGGER_PACKAGE = "debugpy" DEBUGGER_PYTHON_ABI_VERSIONS = ("cp310",) -DEBUGGER_VERSION = "1.8.0" # can also be "latest" +DEBUGGER_VERSION = "1.8.1" # can also be "latest" def _contains(s, parts=()): From d5e98501a5f97c27218b2ba6fd13d7376e438a9e Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:03:25 -0800 Subject: [PATCH 032/767] Update version main branch to pre release (#22976) Update version main branch to pre release --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 871489b89077..a1f8267ffc06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "python", - "version": "2024.2.0-rc", + "version": "2024.3.0-dev", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "python", - "version": "2024.2.0-rc", + "version": "2024.3.0-dev", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index 02339c7e00f2..134f302a38a4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "python", "displayName": "Python", "description": "IntelliSense (Pylance), Linting, Debugging (Python Debugger), code formatting, refactoring, unit tests, and more.", - "version": "2024.2.0-rc", + "version": "2024.3.0-dev", "featureFlags": { "usingNewInterpreterStorage": true }, From f1d7799ca33fd5710e4a553c9acf930f5e16fe5d Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 26 Feb 2024 20:25:07 -0800 Subject: [PATCH 033/767] Update release_plan.md (#22973) add calendar for primary and secondary release champ --- .github/release_plan.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/release_plan.md b/.github/release_plan.md index 71f8d8aa0955..f1d95d8577d0 100644 --- a/.github/release_plan.md +++ b/.github/release_plan.md @@ -1,14 +1,34 @@ +### General Notes All dates should align with VS Code's [iteration](https://github.com/microsoft/vscode/labels/iteration-plan) and [endgame](https://github.com/microsoft/vscode/labels/endgame-plan) plans. Feature freeze is Monday @ 17:00 America/Vancouver, XXX XX. At that point, commits to `main` should only be in response to bugs found during endgame testing until the release candidate is ready. +
+ Release Primary and Secondary Assignments for the 2024 Calendar Year -NOTE: the number of this release is in the issue title and can be substituted in wherever you see [YYYY.minor]. +| Month | Primary | Secondary | +|:----------|:----------|:------------| +| ~~January~~ | ~~Eleanor~~ | ~~Karthik~~ | +| February | Kartik | Anthony | +| March | Karthik | Eleanor | +| April | Paula | Kartik | +| May | Anthony | Karthik | +| June | Eleanor | Paula | +| July | Anthony | Karthik | +| August | Paula | Kartik | +| September | Anthony | Eleanor | +| October | Paula | Kartik | +| November | Kartik | Eleanor | +| December | Karthik | Anthony | + +
# Release candidate (Monday, XXX XX) NOTE: Third Party Notices are automatically added by our build pipelines using https://tools.opensource.microsoft.com/notice. +NOTE: the number of this release is in the issue title and can be substituted in wherever you see [YYYY.minor]. + ### Step 1: ##### Bump the version of `main` to be a release candidate (also updating debugpy dependences, third party notices, and package-lock.json).❄️ (steps with ❄️ will dictate this step happens while main is frozen 🥶) From d0a07de90b5cce0a0047093cc26ec7af15e3c1c1 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 27 Feb 2024 21:11:21 +0530 Subject: [PATCH 034/767] Log best effort versions calculated for an environment (#22980) For https://github.com/microsoft/vscode-python/issues/22877 --- src/client/pythonEnvironments/common/commonUtils.ts | 5 ++++- .../envCollectionActivation/shellIntegrationService.ts | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/pythonEnvironments/common/commonUtils.ts b/src/client/pythonEnvironments/common/commonUtils.ts index 85462531e5e3..4bd94e0402ab 100644 --- a/src/client/pythonEnvironments/common/commonUtils.ts +++ b/src/client/pythonEnvironments/common/commonUtils.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { convertFileType, DirEntry, FileType, getFileFilter, getFileType } from '../../common/utils/filesystem'; import { getOSType, OSType } from '../../common/utils/platform'; -import { traceError } from '../../logging'; +import { traceError, traceVerbose } from '../../logging'; import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../base/info'; import { comparePythonVersionSpecificity } from '../base/info/env'; import { parseVersion } from '../base/info/pythonVersion'; @@ -246,8 +246,11 @@ export async function getPythonVersionFromPath(interpreterPath: string, hint?: s versionA = UNKNOWN_PYTHON_VERSION; } const versionB = interpreterPath ? await getPythonVersionFromNearByFiles(interpreterPath) : UNKNOWN_PYTHON_VERSION; + traceVerbose('Best effort version B for', interpreterPath, JSON.stringify(versionB)); const versionC = interpreterPath ? await getPythonVersionFromPyvenvCfg(interpreterPath) : UNKNOWN_PYTHON_VERSION; + traceVerbose('Best effort version C for', interpreterPath, JSON.stringify(versionC)); const versionD = interpreterPath ? await getPythonVersionFromConda(interpreterPath) : UNKNOWN_PYTHON_VERSION; + traceVerbose('Best effort version D for', interpreterPath, JSON.stringify(versionD)); let version = UNKNOWN_PYTHON_VERSION; for (const v of [versionA, versionB, versionC, versionD]) { diff --git a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts index cba2ccbc6867..8ab3d84122b7 100644 --- a/src/client/terminals/envCollectionActivation/shellIntegrationService.ts +++ b/src/client/terminals/envCollectionActivation/shellIntegrationService.ts @@ -57,7 +57,6 @@ export class ShellIntegrationService implements IShellIntegrationService { } this.appShell.onDidWriteTerminalData( (e) => { - traceVerbose(e.data); // Log this temporarily for analysis if (e.data.includes('\x1b]633;A\x07') || e.data.includes('\x1b]133;A\x07')) { let { shell } = this.appEnvironment; if ('shellPath' in e.terminal.creationOptions && e.terminal.creationOptions.shellPath) { From 724415ddb199f2c5915c967f7a7d2b58ae14e3af Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Wed, 28 Feb 2024 18:28:22 +0530 Subject: [PATCH 035/767] Log environment collection activity at info level (#22990) --- .../terminals/envCollectionActivation/service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index 3ca0b39582ae..447150c0f818 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -26,7 +26,7 @@ import { IPathUtils, } from '../../common/types'; import { Interpreters } from '../../common/utils/localize'; -import { traceError, traceInfo, traceVerbose, traceWarn } from '../../logging'; +import { traceError, traceInfo, traceLog, traceVerbose, traceWarn } from '../../logging'; import { IInterpreterService } from '../../interpreter/contracts'; import { defaultShells } from '../../interpreter/activation/service'; import { IEnvironmentActivationService } from '../../interpreter/activation/types'; @@ -222,7 +222,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (value !== undefined) { if (key === 'PS1') { // We cannot have the full PS1 without executing in terminal, which we do not. Hence prepend it. - traceVerbose( + traceLog( `Prepending environment variable ${key} in collection with ${value} ${JSON.stringify( defaultPrependOptions, )}`, @@ -242,7 +242,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (deactivate) { value = `${deactivate}${this.separator}${value}`; } - traceVerbose( + traceLog( `Prepending environment variable ${key} in collection with ${value} ${JSON.stringify( options, )}`, @@ -255,7 +255,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ if (deactivate) { value = `${deactivate}${this.separator}${value}`; } - traceVerbose( + traceLog( `Prepending environment variable ${key} in collection to ${value} ${JSON.stringify( options, )}`, @@ -268,7 +268,7 @@ export class TerminalEnvVarCollectionService implements IExtensionActivationServ applyAtShellIntegration: true, applyAtProcessCreation: true, }; - traceVerbose( + traceLog( `Setting environment variable ${key} in collection to ${value} ${JSON.stringify(options)}`, ); envVarCollection.replace(key, value, options); From eff0b3b6236db18bd825c1c038d9a8e80948b2d8 Mon Sep 17 00:00:00 2001 From: Erik De Bonte Date: Wed, 28 Feb 2024 17:45:27 -0800 Subject: [PATCH 036/767] Remove old Pylance-related code (#22985) Part of https://github.com/microsoft/vscode-jupyter/issues/14977 Removes some effectively dead code that would only be used by very old (year+) versions of Pylance. This logic has all been moved into vscode-pylance: - Middleware to make the interactive window input box look like a notebook cell - Call to Jupyter to get pythonpath for notebooks. - Configuration hook to get `[python]` value of `editor.formatOnType`. - LSP notebook experiment --- .../languageClientMiddlewareBase.ts | 10 +- .../node/languageClientMiddleware.ts | 87 +------- .../lspInteractiveWindowMiddlewareAddon.ts | 187 ------------------ .../activation/node/lspNotebooksExperiment.ts | 60 ------ src/client/activation/node/manager.ts | 6 +- src/client/activation/node/pylanceApi.ts | 1 - src/client/activation/serviceRegistry.ts | 3 - src/client/jupyter/jupyterIntegration.ts | 33 ---- ...eractiveWindowMiddlewareAddon.unit.test.ts | 175 ---------------- 9 files changed, 4 insertions(+), 558 deletions(-) delete mode 100644 src/client/activation/node/lspInteractiveWindowMiddlewareAddon.ts delete mode 100644 src/client/activation/node/lspNotebooksExperiment.ts delete mode 100644 src/test/activation/node/lspInteractiveWindowMiddlewareAddon.unit.test.ts diff --git a/src/client/activation/languageClientMiddlewareBase.ts b/src/client/activation/languageClientMiddlewareBase.ts index 5f7b6fa72656..f1e102a4081d 100644 --- a/src/client/activation/languageClientMiddlewareBase.ts +++ b/src/client/activation/languageClientMiddlewareBase.ts @@ -87,10 +87,7 @@ export class LanguageClientMiddlewareBase implements Middleware { const settingDict: LSPObject & { pythonPath: string; _envPYTHONPATH: string } = settings[ i ] as LSPObject & { pythonPath: string; _envPYTHONPATH: string }; - settingDict.pythonPath = - (await this.getPythonPathOverride(uri)) ?? - (await interpreterService.getActiveInterpreter(uri))?.path ?? - 'python'; + settingDict.pythonPath = (await interpreterService.getActiveInterpreter(uri))?.path ?? 'python'; const env = await envService.getEnvironmentVariables(uri); const envPYTHONPATH = env.PYTHONPATH; @@ -106,11 +103,6 @@ export class LanguageClientMiddlewareBase implements Middleware { }, }; - // eslint-disable-next-line class-methods-use-this - protected async getPythonPathOverride(_uri: Uri | undefined): Promise { - return undefined; - } - // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-empty-function protected configurationHook(_item: ConfigurationItem, _settings: LSPObject): void {} diff --git a/src/client/activation/node/languageClientMiddleware.ts b/src/client/activation/node/languageClientMiddleware.ts index fbc534f17e1c..1a5da3d1a349 100644 --- a/src/client/activation/node/languageClientMiddleware.ts +++ b/src/client/activation/node/languageClientMiddleware.ts @@ -1,104 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Uri } from 'vscode'; -import { ConfigurationItem, LanguageClient, LSPObject } from 'vscode-languageclient/node'; -import { IJupyterExtensionDependencyManager, IWorkspaceService } from '../../common/application/types'; +import { IJupyterExtensionDependencyManager } from '../../common/application/types'; import { IServiceContainer } from '../../ioc/types'; -import { JupyterExtensionIntegration } from '../../jupyter/jupyterIntegration'; -import { traceLog } from '../../logging'; import { LanguageClientMiddleware } from '../languageClientMiddleware'; -import { LspInteractiveWindowMiddlewareAddon } from './lspInteractiveWindowMiddlewareAddon'; import { LanguageServerType } from '../types'; -import { LspNotebooksExperiment } from './lspNotebooksExperiment'; - export class NodeLanguageClientMiddleware extends LanguageClientMiddleware { - private readonly lspNotebooksExperiment: LspNotebooksExperiment; - - private readonly jupyterExtensionIntegration: JupyterExtensionIntegration; - - private readonly workspaceService: IWorkspaceService; - - public constructor( - serviceContainer: IServiceContainer, - private getClient: () => LanguageClient | undefined, - serverVersion?: string, - ) { + public constructor(serviceContainer: IServiceContainer, serverVersion?: string) { super(serviceContainer, LanguageServerType.Node, serverVersion); - this.workspaceService = serviceContainer.get(IWorkspaceService); - - this.lspNotebooksExperiment = serviceContainer.get(LspNotebooksExperiment); this.setupHidingMiddleware(serviceContainer); - - this.jupyterExtensionIntegration = serviceContainer.get( - JupyterExtensionIntegration, - ); - if (!this.notebookAddon) { - this.notebookAddon = new LspInteractiveWindowMiddlewareAddon( - this.getClient, - this.jupyterExtensionIntegration, - ); - } } // eslint-disable-next-line class-methods-use-this protected shouldCreateHidingMiddleware(_: IJupyterExtensionDependencyManager): boolean { return false; } - - protected async onExtensionChange(jupyterDependencyManager: IJupyterExtensionDependencyManager): Promise { - if (jupyterDependencyManager && jupyterDependencyManager.isJupyterExtensionInstalled) { - await this.lspNotebooksExperiment.onJupyterInstalled(); - } - - if (!this.notebookAddon) { - this.notebookAddon = new LspInteractiveWindowMiddlewareAddon( - this.getClient, - this.jupyterExtensionIntegration, - ); - } - } - - protected async getPythonPathOverride(uri: Uri | undefined): Promise { - if (!uri) { - return undefined; - } - - const jupyterPythonPathFunction = this.jupyterExtensionIntegration.getJupyterPythonPathFunction(); - if (!jupyterPythonPathFunction) { - return undefined; - } - - const result = await jupyterPythonPathFunction(uri); - - if (result) { - traceLog(`Jupyter provided interpreter path override: ${result}`); - } - - return result; - } - - // eslint-disable-next-line class-methods-use-this - protected configurationHook(item: ConfigurationItem, settings: LSPObject): void { - if (item.section === 'editor') { - if (this.workspaceService) { - // Get editor.formatOnType using Python language id so [python] setting - // will be honored if present. - const editorConfig = this.workspaceService.getConfiguration( - item.section, - undefined, - /* languageSpecific */ true, - ); - - const settingDict: LSPObject & { formatOnType?: boolean } = settings as LSPObject & { - formatOnType: boolean; - }; - - settingDict.formatOnType = editorConfig.get('formatOnType'); - } - } - } } diff --git a/src/client/activation/node/lspInteractiveWindowMiddlewareAddon.ts b/src/client/activation/node/lspInteractiveWindowMiddlewareAddon.ts deleted file mode 100644 index 0a40915d98eb..000000000000 --- a/src/client/activation/node/lspInteractiveWindowMiddlewareAddon.ts +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import { Disposable, NotebookCell, NotebookDocument, TextDocument, TextDocumentChangeEvent, Uri } from 'vscode'; -import { Converter } from 'vscode-languageclient/lib/common/codeConverter'; -import { - DidChangeNotebookDocumentNotification, - LanguageClient, - Middleware, - NotebookCellKind, - NotebookDocumentChangeEvent, -} from 'vscode-languageclient/node'; -import * as proto from 'vscode-languageserver-protocol'; -import { JupyterExtensionIntegration } from '../../jupyter/jupyterIntegration'; - -type TextContent = Required['cells']>['textContent']>[0]; - -/** - * Detects the input box text documents of Interactive Windows and makes them appear to be - * the last cell of their corresponding notebooks. - */ -export class LspInteractiveWindowMiddlewareAddon implements Middleware, Disposable { - constructor( - private readonly getClient: () => LanguageClient | undefined, - private readonly jupyterExtensionIntegration: JupyterExtensionIntegration, - ) { - // Make sure a bunch of functions are bound to this. VS code can call them without a this context - this.didOpen = this.didOpen.bind(this); - this.didChange = this.didChange.bind(this); - this.didClose = this.didClose.bind(this); - } - - public dispose(): void { - // Nothing to dispose at the moment - } - - // Map of document URIs to NotebookDocuments for all known notebooks. - private notebookDocumentMap: Map = new Map(); - - // Map of document URIs to TextDocuments that should be linked to a notebook - // whose didOpen we're expecting to see in the future. - private unlinkedTextDocumentMap: Map = new Map(); - - public async didOpen(document: TextDocument, next: (ev: TextDocument) => Promise): Promise { - const notebookUri = this.getNotebookUriForTextDocumentUri(document.uri); - if (!notebookUri) { - await next(document); - return; - } - - const notebookDocument = this.notebookDocumentMap.get(notebookUri.toString()); - if (!notebookDocument) { - this.unlinkedTextDocumentMap.set(notebookUri.toString(), document); - return; - } - - try { - const result: NotebookDocumentChangeEvent = { - cells: { - structure: { - array: { - start: notebookDocument.cellCount, - deleteCount: 0, - cells: [{ kind: NotebookCellKind.Code, document: document.uri.toString() }], - }, - didOpen: [ - { - uri: document.uri.toString(), - languageId: document.languageId, - version: document.version, - text: document.getText(), - }, - ], - didClose: undefined, - }, - }, - }; - - await this.getClient()?.sendNotification(DidChangeNotebookDocumentNotification.type, { - notebookDocument: { version: notebookDocument.version, uri: notebookUri.toString() }, - change: result, - }); - } catch (error) { - this.getClient()?.error('Sending DidChangeNotebookDocumentNotification failed', error); - throw error; - } - } - - public async didChange( - event: TextDocumentChangeEvent, - next: (ev: TextDocumentChangeEvent) => Promise, - ): Promise { - const notebookUri = this.getNotebookUriForTextDocumentUri(event.document.uri); - if (!notebookUri) { - await next(event); - return; - } - - const notebookDocument = this.notebookDocumentMap.get(notebookUri.toString()); - if (notebookDocument) { - const client = this.getClient(); - if (client) { - client.sendNotification(proto.DidChangeNotebookDocumentNotification.type, { - notebookDocument: { uri: notebookUri.toString(), version: notebookDocument.version }, - change: { - cells: { - textContent: [ - LspInteractiveWindowMiddlewareAddon._asTextContentChange( - event, - client.code2ProtocolConverter, - ), - ], - }, - }, - }); - } - } - } - - private static _asTextContentChange(event: TextDocumentChangeEvent, c2pConverter: Converter): TextContent { - const params = c2pConverter.asChangeTextDocumentParams(event, event.document.uri, event.document.version); - return { document: params.textDocument, changes: params.contentChanges }; - } - - public async didClose(document: TextDocument, next: (ev: TextDocument) => Promise): Promise { - const notebookUri = this.getNotebookUriForTextDocumentUri(document.uri); - if (!notebookUri) { - await next(document); - return; - } - - this.unlinkedTextDocumentMap.delete(notebookUri.toString()); - } - - public async didOpenNotebook( - notebookDocument: NotebookDocument, - cells: NotebookCell[], - next: (notebookDocument: NotebookDocument, cells: NotebookCell[]) => Promise, - ): Promise { - this.notebookDocumentMap.set(notebookDocument.uri.toString(), notebookDocument); - - const relatedTextDocument = this.unlinkedTextDocumentMap.get(notebookDocument.uri.toString()); - if (relatedTextDocument) { - const newCells = [ - ...cells, - { - index: notebookDocument.cellCount, - notebook: notebookDocument, - kind: NotebookCellKind.Code, - document: relatedTextDocument, - metadata: {}, - outputs: [], - executionSummary: undefined, - }, - ]; - - this.unlinkedTextDocumentMap.delete(notebookDocument.uri.toString()); - - await next(notebookDocument, newCells); - } else { - await next(notebookDocument, cells); - } - } - - public async didCloseNotebook( - notebookDocument: NotebookDocument, - cells: NotebookCell[], - next: (notebookDocument: NotebookDocument, cells: NotebookCell[]) => Promise, - ): Promise { - this.notebookDocumentMap.delete(notebookDocument.uri.toString()); - - await next(notebookDocument, cells); - } - - notebooks = { - didOpen: this.didOpenNotebook.bind(this), - didClose: this.didCloseNotebook.bind(this), - }; - - private getNotebookUriForTextDocumentUri(textDocumentUri: Uri): Uri | undefined { - const getNotebookUriFunction = this.jupyterExtensionIntegration.getGetNotebookUriForTextDocumentUriFunction(); - if (!getNotebookUriFunction) { - return undefined; - } - - return getNotebookUriFunction(textDocumentUri); - } -} diff --git a/src/client/activation/node/lspNotebooksExperiment.ts b/src/client/activation/node/lspNotebooksExperiment.ts deleted file mode 100644 index de0acde0600e..000000000000 --- a/src/client/activation/node/lspNotebooksExperiment.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import { inject, injectable } from 'inversify'; -import { IExtensionSingleActivationService } from '../types'; -import { traceVerbose } from '../../logging'; -import { IJupyterExtensionDependencyManager } from '../../common/application/types'; -import { IServiceContainer } from '../../ioc/types'; -import { sleep } from '../../common/utils/async'; -import { JupyterExtensionIntegration } from '../../jupyter/jupyterIntegration'; - -@injectable() -export class LspNotebooksExperiment implements IExtensionSingleActivationService { - public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; - - private isJupyterInstalled = false; - - constructor( - @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer, - @inject(IJupyterExtensionDependencyManager) jupyterDependencyManager: IJupyterExtensionDependencyManager, - ) { - this.isJupyterInstalled = jupyterDependencyManager.isJupyterExtensionInstalled; - } - - // eslint-disable-next-line class-methods-use-this - public activate(): Promise { - return Promise.resolve(); - } - - public async onJupyterInstalled(): Promise { - if (this.isJupyterInstalled) { - return; - } - - await this.waitForJupyterToRegisterPythonPathFunction(); - - this.isJupyterInstalled = true; - } - - private async waitForJupyterToRegisterPythonPathFunction(): Promise { - const jupyterExtensionIntegration = this.serviceContainer.get( - JupyterExtensionIntegration, - ); - - let success = false; - for (let tryCount = 0; tryCount < 20; tryCount += 1) { - const jupyterPythonPathFunction = jupyterExtensionIntegration.getJupyterPythonPathFunction(); - if (jupyterPythonPathFunction) { - traceVerbose(`Jupyter called registerJupyterPythonPathFunction`); - success = true; - break; - } - - await sleep(500); - } - - if (!success) { - traceVerbose(`Timed out waiting for Jupyter to call registerJupyterPythonPathFunction`); - } - } -} diff --git a/src/client/activation/node/manager.ts b/src/client/activation/node/manager.ts index b85d8fe6ed14..5a66e4abecd0 100644 --- a/src/client/activation/node/manager.ts +++ b/src/client/activation/node/manager.ts @@ -116,11 +116,7 @@ export class NodeLanguageServerManager implements ILanguageServerManager { @traceDecoratorVerbose('Starting language server') protected async startLanguageServer(): Promise { const options = await this.analysisOptions.getAnalysisOptions(); - this.middleware = new NodeLanguageClientMiddleware( - this.serviceContainer, - () => this.languageServerProxy.languageClient, - this.lsVersion, - ); + this.middleware = new NodeLanguageClientMiddleware(this.serviceContainer, this.lsVersion); options.middleware = this.middleware; // Make sure the middleware is connected if we restart and we we're already connected. diff --git a/src/client/activation/node/pylanceApi.ts b/src/client/activation/node/pylanceApi.ts index 72f20db140e4..4b3d21d7527e 100644 --- a/src/client/activation/node/pylanceApi.ts +++ b/src/client/activation/node/pylanceApi.ts @@ -18,7 +18,6 @@ export interface PylanceApi { }; notebook?: { registerJupyterPythonPathFunction(func: (uri: Uri) => Promise): void; - registerGetNotebookUriForTextDocumentUriFunction(func: (textDocumentUri: Uri) => Uri | undefined): void; getCompletionItems( document: TextDocument, position: Position, diff --git a/src/client/activation/serviceRegistry.ts b/src/client/activation/serviceRegistry.ts index aed2d2e346e4..a52a6555236d 100644 --- a/src/client/activation/serviceRegistry.ts +++ b/src/client/activation/serviceRegistry.ts @@ -15,7 +15,6 @@ import { LoadLanguageServerExtension } from './common/loadLanguageServerExtensio import { PartialModeStatusItem } from './partialModeStatus'; import { ILanguageServerWatcher } from '../languageServer/types'; import { LanguageServerWatcher } from '../languageServer/watcher'; -import { LspNotebooksExperiment } from './node/lspNotebooksExperiment'; export function registerTypes(serviceManager: IServiceManager): void { serviceManager.addSingleton(IExtensionActivationService, PartialModeStatusItem); @@ -35,6 +34,4 @@ export function registerTypes(serviceManager: IServiceManager): void { serviceManager.addSingleton(ILanguageServerWatcher, LanguageServerWatcher); serviceManager.addBinding(ILanguageServerWatcher, IExtensionActivationService); - serviceManager.addSingleton(LspNotebooksExperiment, LspNotebooksExperiment); - serviceManager.addBinding(LspNotebooksExperiment, IExtensionSingleActivationService); } diff --git a/src/client/jupyter/jupyterIntegration.ts b/src/client/jupyter/jupyterIntegration.ts index dbfd1bdf5681..9faab6d2a763 100644 --- a/src/client/jupyter/jupyterIntegration.ts +++ b/src/client/jupyter/jupyterIntegration.ts @@ -57,14 +57,6 @@ type PythonApiForJupyterExtension = { * @param func : The function that Python should call when requesting the Python path. */ registerJupyterPythonPathFunction(func: (uri: Uri) => Promise): void; - - /** - * Call to provide a function that the Python extension can call to request the notebook - * document URI related to a particular text document URI, or undefined if there is no - * associated notebook. - * @param func : The function that Python should call when requesting the notebook URI. - */ - registerGetNotebookUriForTextDocumentUriFunction(func: (textDocumentUri: Uri) => Uri | undefined): void; }; type JupyterExtensionApi = { @@ -81,10 +73,6 @@ export class JupyterExtensionIntegration { private pylanceExtension: Extension | undefined; - private jupyterPythonPathFunction: ((uri: Uri) => Promise) | undefined; - - private getNotebookUriForTextDocumentUriFunction: ((textDocumentUri: Uri) => Uri | undefined) | undefined; - constructor( @inject(IExtensions) private readonly extensions: IExtensions, @inject(IInterpreterSelector) private readonly interpreterSelector: IInterpreterSelector, @@ -123,8 +111,6 @@ export class JupyterExtensionIntegration { getCondaVersion: () => this.condaService.getCondaVersion(), registerJupyterPythonPathFunction: (func: (uri: Uri) => Promise) => this.registerJupyterPythonPathFunction(func), - registerGetNotebookUriForTextDocumentUriFunction: (func: (textDocumentUri: Uri) => Uri | undefined) => - this.registerGetNotebookUriForTextDocumentUriFunction(func), }); return undefined; } @@ -169,28 +155,9 @@ export class JupyterExtensionIntegration { } private registerJupyterPythonPathFunction(func: (uri: Uri) => Promise) { - this.jupyterPythonPathFunction = func; - const api = this.getPylanceApi(); if (api) { api.notebook!.registerJupyterPythonPathFunction(func); } } - - public getJupyterPythonPathFunction(): ((uri: Uri) => Promise) | undefined { - return this.jupyterPythonPathFunction; - } - - public registerGetNotebookUriForTextDocumentUriFunction(func: (textDocumentUri: Uri) => Uri | undefined): void { - this.getNotebookUriForTextDocumentUriFunction = func; - - const api = this.getPylanceApi(); - if (api) { - api.notebook!.registerGetNotebookUriForTextDocumentUriFunction(func); - } - } - - public getGetNotebookUriForTextDocumentUriFunction(): ((textDocumentUri: Uri) => Uri | undefined) | undefined { - return this.getNotebookUriForTextDocumentUriFunction; - } } diff --git a/src/test/activation/node/lspInteractiveWindowMiddlewareAddon.unit.test.ts b/src/test/activation/node/lspInteractiveWindowMiddlewareAddon.unit.test.ts deleted file mode 100644 index 32d9198ef7ba..000000000000 --- a/src/test/activation/node/lspInteractiveWindowMiddlewareAddon.unit.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { NotebookCell, NotebookCellKind, NotebookDocument, TextDocument, Uri } from 'vscode'; -import { expect } from 'chai'; -import { anything, capture, instance, mock, verify, when } from 'ts-mockito'; -import { LanguageClient } from 'vscode-languageclient/node'; -import { LspInteractiveWindowMiddlewareAddon } from '../../../client/activation/node/lspInteractiveWindowMiddlewareAddon'; -import { JupyterExtensionIntegration } from '../../../client/jupyter/jupyterIntegration'; -import { IExtensions } from '../../../client/common/types'; -import { ICondaService, IInterpreterDisplay } from '../../../client/interpreter/contracts'; -import { IInterpreterSelector } from '../../../client/interpreter/configuration/types'; -import { IEnvironmentActivationService } from '../../../client/interpreter/activation/types'; -import { IContextKeyManager, IWorkspaceService } from '../../../client/common/application/types'; -import { MockMemento } from '../../mocks/mementos'; - -suite('Pylance Language Server - Interactive Window LSP Notebooks', () => { - const languageClientMock = mock(); - let languageClient: LanguageClient; - let jupyterApi: JupyterExtensionIntegration; - let middleware: LspInteractiveWindowMiddlewareAddon; - - setup(() => { - languageClient = instance(languageClientMock); - jupyterApi = new JupyterExtensionIntegration( - mock(), - mock(), - mock(), - new MockMemento(), - mock(), - mock(), - mock(), - mock(), - ); - jupyterApi.registerGetNotebookUriForTextDocumentUriFunction(getNotebookUriFunction); - }); - teardown(() => { - middleware?.dispose(); - }); - - test('Unrelated document open should be forwarded to next handler unchanged', async () => { - middleware = makeMiddleware(); - - const uri = Uri.from({ scheme: 'file', path: 'test.py' }); - const textDocument = createTextDocument(uri); - - let nextCalled = false; - await middleware.didOpen(textDocument, async (_) => { - nextCalled = true; - }); - - return expect(nextCalled).to.be.true; - }); - - test('Notebook-related textDocument/didOpen should be swallowed', async () => { - middleware = makeMiddleware(); - - const uri = Uri.from({ scheme: 'test-input', path: 'Test' }); - const textDocument = createTextDocument(uri); - - let nextCalled = false; - await middleware.didOpen(textDocument, async (_) => { - nextCalled = true; - }); - - return expect(nextCalled).to.be.false; - }); - - test('Notebook-related document should be added at end of cells in notebookDocument/didOpen', async () => { - middleware = makeMiddleware(); - - const uri = Uri.from({ scheme: 'test-input', path: 'Test' }); - const textDocument = createTextDocument(uri); - - await middleware.didOpen(textDocument, async (_) => {}); - - const cellCount = 2; - const [notebookDocument, cells] = createNotebookDocument(getNotebookUriFunction(uri)!, cellCount); - await middleware.notebooks.didOpen(notebookDocument, cells, async (_, nextCells) => { - expect(nextCells.length).to.be.equals(cellCount + 1); - expect(nextCells[cellCount]).to.deep.equal({ - index: cellCount, - notebook: notebookDocument, - kind: NotebookCellKind.Code, - document: textDocument, - metadata: {}, - outputs: [], - executionSummary: undefined, - }); - }); - }); - - test('Notebook-related document opened after notebook causes notebookDocument/didChange', async () => { - middleware = makeMiddleware(); - - const uri = Uri.from({ scheme: 'test-input', path: 'Test' }); - const textDocument = createTextDocument(uri); - - const cellCount = 2; - const [notebookDocument, cells] = createNotebookDocument(getNotebookUriFunction(uri)!, cellCount); - await middleware.notebooks.didOpen(notebookDocument, cells, async (_) => {}); - - await middleware.didOpen(textDocument, async (_) => {}); - - verify(languageClientMock.sendNotification(anything(), anything())).once(); - const message = capture(languageClientMock.sendNotification).last()[1]; - - expect(message.notebookDocument.uri).to.equal(notebookDocument.uri.toString()); - expect(message.change.cells.structure).to.deep.equal({ - array: { - start: notebookDocument.cellCount, - deleteCount: 0, - cells: [{ kind: NotebookCellKind.Code, document: textDocument.uri.toString() }], - }, - didOpen: [ - { - uri: textDocument.uri.toString(), - languageId: textDocument.languageId, - version: textDocument.version, - text: textDocument.getText(), - }, - ], - didClose: undefined, - }); - }); - - function makeMiddleware(): LspInteractiveWindowMiddlewareAddon { - return new LspInteractiveWindowMiddlewareAddon(() => languageClient, jupyterApi); - } - - function getNotebookUriFunction(textDocumentUri: Uri): Uri | undefined { - if (textDocumentUri.scheme === 'test-input') { - return textDocumentUri.with({ scheme: 'test-notebook' }); - } - - return undefined; - } - - function createTextDocument(uri: Uri): TextDocument { - const textDocumentMock = mock(); - when(textDocumentMock.uri).thenReturn(uri); - when(textDocumentMock.languageId).thenReturn('python'); - when(textDocumentMock.version).thenReturn(11); - - return instance(textDocumentMock); - } - - function createNotebookDocument(uri: Uri, cellCount: number): [NotebookDocument, NotebookCell[]] { - const notebookDocumentMock = mock(); - when(notebookDocumentMock.uri).thenReturn(uri); - when(notebookDocumentMock.notebookType).thenReturn('jupyter'); - when(notebookDocumentMock.version).thenReturn(20); - when(notebookDocumentMock.cellCount).thenReturn(cellCount); - - const notebookDocument = instance(notebookDocumentMock); - - const cells: NotebookCell[] = []; - for (let i = 0; i < cellCount; i = i + 1) { - cells.push({ - index: i, - notebook: notebookDocument, - kind: NotebookCellKind.Code, - document: createTextDocument(Uri.from({ scheme: 'test-cell', path: `cell${i}` })), - metadata: {}, - outputs: [], - executionSummary: undefined, - }); - } - - return [notebookDocument, cells]; - } -}); From 608effb96f82b1491a9eb73445d2f0b09f7034c1 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 29 Feb 2024 21:47:58 +1100 Subject: [PATCH 037/767] Remove sync file operation to unblock ext host (#22997) For https://github.com/microsoft/vscode-python/issues/22991 --- .../common/environmentManagers/poetry.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/pythonEnvironments/common/environmentManagers/poetry.ts b/src/client/pythonEnvironments/common/environmentManagers/poetry.ts index 48199b5bdc8f..5e5fa2416208 100644 --- a/src/client/pythonEnvironments/common/environmentManagers/poetry.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/poetry.ts @@ -10,7 +10,7 @@ import { isParentPath, pathExists, pathExistsSync, - readFileSync, + readFile, shellExecute, } from '../externalDependencies'; import { getEnvironmentDirFromPath } from '../commonUtils'; @@ -63,7 +63,7 @@ async function isLocalPoetryEnvironment(interpreterPath: string): Promise { // Following check should be performed synchronously so we trigger poetry execution as soon as possible. - if (!hasValidPyprojectToml(cwd)) { + if (!(await hasValidPyprojectToml(cwd))) { // This check is not expensive and may change during a session, so we need not cache it. return undefined; } @@ -325,12 +325,12 @@ export async function isPoetryEnvironmentRelatedToFolder( * * @param folder Folder to look for pyproject.toml file in. */ -function hasValidPyprojectToml(folder: string): boolean { +async function hasValidPyprojectToml(folder: string): Promise { const pyprojectToml = path.join(folder, 'pyproject.toml'); if (!pathExistsSync(pyprojectToml)) { return false; } - const content = readFileSync(pyprojectToml); + const content = await readFile(pyprojectToml); if (!content.includes('[tool.poetry]')) { return false; } From 8d4de77daf0391f48ced1c4e871884592ad5f184 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Thu, 29 Feb 2024 19:15:11 +0530 Subject: [PATCH 038/767] Remove verbose logging which is not used (#23001) Related: https://github.com/microsoft/vscode-python/issues/22993 --- src/client/common/persistentState.ts | 6 +----- .../base/locators/lowLevel/fsWatchingLocator.ts | 2 -- .../creation/installedPackagesDiagnostic.ts | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/client/common/persistentState.ts b/src/client/common/persistentState.ts index ea86b2e145ba..e82299bfc670 100644 --- a/src/client/common/persistentState.ts +++ b/src/client/common/persistentState.ts @@ -6,7 +6,7 @@ import { inject, injectable, named } from 'inversify'; import { Memento } from 'vscode'; import { IExtensionSingleActivationService } from '../activation/types'; -import { traceError, traceVerbose, traceWarn } from '../logging'; +import { traceError } from '../logging'; import { ICommandManager } from './application/types'; import { Commands } from './constants'; import { @@ -92,12 +92,8 @@ export class PersistentState implements IPersistentState { // Due to a VSCode bug sometimes the changes are not reflected in the storage, atleast not immediately. // It is noticed however that if we reset the storage first and then update it, it works. // https://github.com/microsoft/vscode/issues/171827 - traceVerbose('Storage update failed for key', this.key, ' retrying by resetting first'); await this.updateValue(undefined as any, false); await this.updateValue(newValue, false); - if (JSON.stringify(this.value) != JSON.stringify(newValue)) { - traceWarn('Retry failed, storage update failed for key', this.key); - } } } catch (ex) { traceError('Error while updating storage for key:', this.key, ex); diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts index 7565913f0a72..71bd30f7cfdc 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts @@ -105,9 +105,7 @@ export abstract class FSWatchingLocator extends LazyResourceBasedLocator { } // Start the FS watchers. - traceVerbose('Getting roots'); let roots = await this.getRoots(); - traceVerbose('Found roots'); if (typeof roots === 'string') { roots = [roots]; } diff --git a/src/client/pythonEnvironments/creation/installedPackagesDiagnostic.ts b/src/client/pythonEnvironments/creation/installedPackagesDiagnostic.ts index a46a32ce8276..0a96ff088014 100644 --- a/src/client/pythonEnvironments/creation/installedPackagesDiagnostic.ts +++ b/src/client/pythonEnvironments/creation/installedPackagesDiagnostic.ts @@ -29,7 +29,6 @@ async function setContextForActiveEditor(diagnosticCollection: DiagnosticCollect } // undefined here in the logs means no file was selected - traceVerbose(`Clearing context for python dependencies not installed: ${doc?.uri.fsPath}`); await executeCommand('setContext', DEPS_NOT_INSTALLED_KEY, false); } From 999f7e7ce02c1a116bf0089402987d86e12ee783 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 29 Feb 2024 19:02:11 -0800 Subject: [PATCH 039/767] chore: add APIScan field (#23007) --- build/azure-pipeline.stable.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/azure-pipeline.stable.yml b/build/azure-pipeline.stable.yml index 02f8bd38cf81..8d04ebab318a 100644 --- a/build/azure-pipeline.stable.yml +++ b/build/azure-pipeline.stable.yml @@ -87,3 +87,4 @@ extends: - 'lszomoru@microsoft.com' - 'brcan@microsoft.com' - 'kanadig@microsoft.com' + apiScanSoftwareVersion: '2024' \ No newline at end of file From 50f4b7b62b5e1829dd95c7616a450b2b56d8bd50 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 1 Mar 2024 22:33:32 +0530 Subject: [PATCH 040/767] Make `environments.known` API faster (#23010) Closes https://github.com/microsoft/vscode-python/issues/22994 closes https://github.com/microsoft/vscode-python/issues/22993 Temporarily build our own known cache from which we return envs instead. --- src/client/environmentApi.ts | 38 +++++++++++++++++++++------- src/client/environmentKnownCache.ts | 37 +++++++++++++++++++++++++++ src/test/api.functional.test.ts | 1 + src/test/environmentApi.unit.test.ts | 20 +++++++++------ 4 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 src/client/environmentKnownCache.ts diff --git a/src/client/environmentApi.ts b/src/client/environmentApi.ts index da6a132b2b44..65430920d015 100644 --- a/src/client/environmentApi.ts +++ b/src/client/environmentApi.ts @@ -32,6 +32,7 @@ import { Resource, } from './api/types'; import { buildEnvironmentCreationApi } from './pythonEnvironments/creation/createEnvApi'; +import { EnvironmentKnownCache } from './environmentKnownCache'; type ActiveEnvironmentChangeEvent = { resource: WorkspaceFolder | undefined; @@ -120,6 +121,15 @@ export function buildEnvironmentApi( const disposables = serviceContainer.get(IDisposableRegistry); const extensions = serviceContainer.get(IExtensions); const envVarsProvider = serviceContainer.get(IEnvironmentVariablesProvider); + let knownCache: EnvironmentKnownCache; + + function initKnownCache() { + const knownEnvs = discoveryApi + .getEnvs() + .filter((e) => filterUsingVSCodeContext(e)) + .map((e) => updateReference(e)); + return new EnvironmentKnownCache(knownEnvs); + } function sendApiTelemetry(apiName: string, args?: unknown) { extensions .determineExtensionFromCallStack() @@ -139,10 +149,15 @@ export function buildEnvironmentApi( // Filter out environments that are not in the current workspace. return; } + if (!knownCache) { + knownCache = initKnownCache(); + } if (e.old) { if (e.new) { + const newEnv = updateReference(e.new); + knownCache.updateEnv(convertEnvInfo(e.old), newEnv); traceVerbose('Python API env change detected', env.id, 'update'); - onEnvironmentsChanged.fire({ type: 'update', env: convertEnvInfoAndGetReference(e.new) }); + onEnvironmentsChanged.fire({ type: 'update', env: newEnv }); reportInterpretersChanged([ { path: getEnvPath(e.new.executable.filename, e.new.location).path, @@ -150,8 +165,10 @@ export function buildEnvironmentApi( }, ]); } else { + const oldEnv = updateReference(e.old); + knownCache.updateEnv(oldEnv, undefined); traceVerbose('Python API env change detected', env.id, 'remove'); - onEnvironmentsChanged.fire({ type: 'remove', env: convertEnvInfoAndGetReference(e.old) }); + onEnvironmentsChanged.fire({ type: 'remove', env: oldEnv }); reportInterpretersChanged([ { path: getEnvPath(e.old.executable.filename, e.old.location).path, @@ -160,8 +177,10 @@ export function buildEnvironmentApi( ]); } } else if (e.new) { + const newEnv = updateReference(e.new); + knownCache.addEnv(newEnv); traceVerbose('Python API env change detected', env.id, 'add'); - onEnvironmentsChanged.fire({ type: 'add', env: convertEnvInfoAndGetReference(e.new) }); + onEnvironmentsChanged.fire({ type: 'add', env: newEnv }); reportInterpretersChanged([ { path: getEnvPath(e.new.executable.filename, e.new.location).path, @@ -179,6 +198,9 @@ export function buildEnvironmentApi( onEnvironmentsChanged, onEnvironmentVariablesChanged, ); + if (!knownCache!) { + knownCache = initKnownCache(); + } const environmentApi: PythonExtension['environments'] = { getEnvironmentVariables: (resource?: Resource) => { @@ -234,11 +256,9 @@ export function buildEnvironmentApi( return resolveEnvironment(path, discoveryApi); }, get known(): Environment[] { - sendApiTelemetry('known'); - return discoveryApi - .getEnvs() - .filter((e) => filterUsingVSCodeContext(e)) - .map((e) => convertEnvInfoAndGetReference(e)); + // Do not send telemetry for "known", as this may be called 1000s of times so it can significant: + // sendApiTelemetry('known'); + return knownCache.envs; }, async refreshEnvironments(options?: RefreshOptions) { if (!workspace.isTrusted) { @@ -351,7 +371,7 @@ export function convertEnvInfo(env: PythonEnvInfo): Environment { return convertedEnv as Environment; } -function convertEnvInfoAndGetReference(env: PythonEnvInfo): Environment { +function updateReference(env: PythonEnvInfo): Environment { return getEnvReference(convertEnvInfo(env)); } diff --git a/src/client/environmentKnownCache.ts b/src/client/environmentKnownCache.ts new file mode 100644 index 000000000000..287f5bab343f --- /dev/null +++ b/src/client/environmentKnownCache.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { Environment } from './api/types'; + +/** + * Workaround temp cache until types are consolidated. + */ +export class EnvironmentKnownCache { + private _envs: Environment[] = []; + + constructor(envs: Environment[]) { + this._envs = envs; + } + + public get envs(): Environment[] { + return this._envs; + } + + public addEnv(env: Environment): void { + const found = this._envs.find((e) => env.id === e.id); + if (!found) { + this._envs.push(env); + } + } + + public updateEnv(oldValue: Environment, newValue: Environment | undefined): void { + const index = this._envs.findIndex((e) => oldValue.id === e.id); + if (index !== -1) { + if (newValue === undefined) { + this._envs.splice(index, 1); + } else { + this._envs[index] = newValue; + } + } + } +} diff --git a/src/test/api.functional.test.ts b/src/test/api.functional.test.ts index 74293f55256c..d99bcfde81ab 100644 --- a/src/test/api.functional.test.ts +++ b/src/test/api.functional.test.ts @@ -37,6 +37,7 @@ suite('Extension API', () => { interpreterService = mock(InterpreterService); environmentVariablesProvider = mock(); discoverAPI = mock(); + when(discoverAPI.getEnvs()).thenReturn([]); when(serviceContainer.get(IConfigurationService)).thenReturn( instance(configurationService), diff --git a/src/test/environmentApi.unit.test.ts b/src/test/environmentApi.unit.test.ts index 1d8dc3e5c847..012e1a0bfc69 100644 --- a/src/test/environmentApi.unit.test.ts +++ b/src/test/environmentApi.unit.test.ts @@ -74,8 +74,7 @@ suite('Python Environment API', () => { envVarsProvider = typemoq.Mock.ofType(); extensions .setup((e) => e.determineExtensionFromCallStack()) - .returns(() => Promise.resolve({ extensionId: 'id', displayName: 'displayName', apiName: 'apiName' })) - .verifiable(typemoq.Times.atLeastOnce()); + .returns(() => Promise.resolve({ extensionId: 'id', displayName: 'displayName', apiName: 'apiName' })); interpreterPathService = typemoq.Mock.ofType(); configService = typemoq.Mock.ofType(); onDidChangeRefreshState = new EventEmitter(); @@ -94,13 +93,12 @@ suite('Python Environment API', () => { discoverAPI.setup((d) => d.onProgress).returns(() => onDidChangeRefreshState.event); discoverAPI.setup((d) => d.onChanged).returns(() => onDidChangeEnvironments.event); + discoverAPI.setup((d) => d.getEnvs()).returns(() => []); environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object); }); teardown(() => { - // Verify each API method sends telemetry regarding who called the API. - extensions.verifyAll(); sinon.restore(); }); @@ -325,6 +323,7 @@ suite('Python Environment API', () => { }, ]; discoverAPI.setup((d) => d.getEnvs()).returns(() => envs); + environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object); const actual = environmentApi.known; const actualEnvs = actual?.map((a) => (a as EnvironmentReference).internal); assert.deepEqual( @@ -409,10 +408,10 @@ suite('Python Environment API', () => { // Update events events = []; expectedEvents = []; - const updatedEnv = cloneDeep(envs[0]); - updatedEnv.arch = Architecture.x86; - onDidChangeEnvironments.fire({ old: envs[0], new: updatedEnv }); - expectedEvents.push({ env: convertEnvInfo(updatedEnv), type: 'update' }); + const updatedEnv0 = cloneDeep(envs[0]); + updatedEnv0.arch = Architecture.x86; + onDidChangeEnvironments.fire({ old: envs[0], new: updatedEnv0 }); + expectedEvents.push({ env: convertEnvInfo(updatedEnv0), type: 'update' }); eventValues = events.map((e) => ({ env: (e.env as EnvironmentReference).internal, type: e.type })); assert.deepEqual(eventValues, expectedEvents); @@ -423,6 +422,11 @@ suite('Python Environment API', () => { expectedEvents.push({ env: convertEnvInfo(envs[2]), type: 'remove' }); eventValues = events.map((e) => ({ env: (e.env as EnvironmentReference).internal, type: e.type })); assert.deepEqual(eventValues, expectedEvents); + + const expectedEnvs = [convertEnvInfo(updatedEnv0), convertEnvInfo(envs[1])].sort(); + const knownEnvs = environmentApi.known.map((e) => (e as EnvironmentReference).internal).sort(); + + assert.deepEqual(expectedEnvs, knownEnvs); }); test('updateActiveEnvironmentPath: no resource', async () => { From c330ff9678e5d0735b4c4b318315bcc1708fc9ca Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 1 Mar 2024 22:43:04 +0530 Subject: [PATCH 041/767] Do not use readSync everytime telemetry is sent (#23011) For https://github.com/microsoft/vscode-python/issues/22991 Introduced by https://github.com/microsoft/vscode-python/pull/21377 cc/ @DonJayamanne --- src/client/telemetry/index.ts | 18 +++++++++++------- src/test/telemetry/index.unit.test.ts | 24 ------------------------ 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index bdb098be520e..3237bafc224b 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -3,11 +3,9 @@ // Licensed under the MIT License. import TelemetryReporter from '@vscode/extension-telemetry'; - -import * as path from 'path'; -import * as fs from 'fs-extra'; +import type * as vscodeTypes from 'vscode'; import { DiagnosticCodes } from '../application/diagnostics/constants'; -import { AppinsightsKey, EXTENSION_ROOT_DIR, isTestExecution, isUnitTestExecution } from '../common/constants'; +import { AppinsightsKey, isTestExecution, isUnitTestExecution, PVSC_EXTENSION_ID } from '../common/constants'; import type { TerminalShellType } from '../common/terminal/types'; import { StopWatch } from '../common/utils/stopWatch'; import { isPromise } from '../common/utils/async'; @@ -39,14 +37,20 @@ function isTelemetrySupported(): boolean { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let packageJSON: any; + /** * Checks if the telemetry is disabled * @returns {boolean} */ export function isTelemetryDisabled(): boolean { - const packageJsonPath = path.join(EXTENSION_ROOT_DIR, 'package.json'); - const packageJson = fs.readJSONSync(packageJsonPath); - return !packageJson.enableTelemetry; + if (!packageJSON) { + const vscode = require('vscode') as typeof vscodeTypes; + const pythonExtension = vscode.extensions.getExtension(PVSC_EXTENSION_ID)!; + packageJSON = pythonExtension.packageJSON; + } + return !packageJSON.enableTelemetry; } const sharedProperties: Record = {}; diff --git a/src/test/telemetry/index.unit.test.ts b/src/test/telemetry/index.unit.test.ts index d369dce27855..e155ac0a2092 100644 --- a/src/test/telemetry/index.unit.test.ts +++ b/src/test/telemetry/index.unit.test.ts @@ -10,7 +10,6 @@ import * as fs from 'fs-extra'; import { _resetSharedProperties, clearTelemetryReporter, - isTelemetryDisabled, sendTelemetryEvent, setSharedProperty, } from '../../client/telemetry'; @@ -60,29 +59,6 @@ suite('Telemetry', () => { sinon.restore(); }); - const testsForisTelemetryDisabled = [ - { - testName: 'Returns true', - settings: { enableTelemetry: true }, - expectedResult: false, - }, - { - testName: 'Returns false ', - settings: { enableTelemetry: false }, - expectedResult: true, - }, - ]; - - suite('Function isTelemetryDisabled()', () => { - testsForisTelemetryDisabled.forEach((testParams) => { - test(testParams.testName, async () => { - readJSONSyncStub.returns(testParams.settings); - expect(isTelemetryDisabled()).to.equal(testParams.expectedResult); - sinon.assert.calledOnce(readJSONSyncStub); - }); - }); - }); - test('Send Telemetry', () => { rewiremock.enable(); rewiremock('@vscode/extension-telemetry').with({ default: Reporter }); From 27783cea76a6273da88bf0a8f7813b6ce0d93b61 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 1 Mar 2024 13:27:50 -0800 Subject: [PATCH 042/767] Rename `pythonFiles` to `python_files` (#22921) --- .eslintignore | 2 +- .github/actions/build-vsix/action.yml | 12 +- .github/actions/lint/action.yml | 4 +- .github/actions/smoke-tests/action.yml | 4 +- .github/release_plan.md | 2 +- .github/workflows/build.yml | 26 +- .github/workflows/pr-check.yml | 38 +- .gitignore | 4 +- .vscode/launch.json | 6 +- .vscode/settings.json | 2 +- .vscodeignore | 12 +- build/azure-pipeline.pre-release.yml | 8 +- build/azure-pipeline.stable.yml | 8 +- build/existingFiles.json | 2 +- noxfile.py | 18 +- pythonExtensionApi/src/main.ts | 2 +- {pythonFiles => python_files}/.env | 0 .../.vscode/settings.json | 0 .../Notebooks intro.ipynb | 0 {pythonFiles => python_files}/create_conda.py | 0 .../create_microvenv.py | 0 {pythonFiles => python_files}/create_venv.py | 0 .../deactivate/bash/deactivate | 0 .../deactivate/fish/deactivate | 0 .../deactivate/powershell/deactivate.ps1 | 0 .../deactivate/zsh/deactivate | 0 .../download_get_pip.py | 2 +- .../get_output_via_markers.py | 0 .../install_debugpy.py | 2 +- .../installed_check.py | 0 .../interpreterInfo.py | 0 .../jedilsp_requirements/requirements.in | 2 +- .../jedilsp_requirements/requirements.txt | 6 +- {pythonFiles => python_files}/linter.py | 0 .../normalizeSelection.py | 0 .../printEnvVariables.py | 0 .../printEnvVariablesToFile.py | 0 {pythonFiles => python_files}/pyproject.toml | 10 +- {pythonFiles => python_files}/pythonrc.py | 0 .../run-jedi-language-server.py | 2 +- {pythonFiles => python_files}/shell_exec.py | 0 .../tensorboard_launcher.py | 0 .../testing_tools/__init__.py | 0 .../testing_tools/adapter/__init__.py | 0 .../testing_tools/adapter/__main__.py | 0 .../testing_tools/adapter/discovery.py | 0 .../testing_tools/adapter/errors.py | 0 .../testing_tools/adapter/info.py | 0 .../testing_tools/adapter/pytest/__init__.py | 0 .../testing_tools/adapter/pytest/_cli.py | 0 .../adapter/pytest/_discovery.py | 0 .../adapter/pytest/_pytest_item.py | 0 .../testing_tools/adapter/report.py | 0 .../testing_tools/adapter/util.py | 0 .../testing_tools/process_json_util.py | 0 .../testing_tools/run_adapter.py | 4 +- .../testing_tools/socket_manager.py | 0 .../testing_tools/unittest_discovery.py | 0 {pythonFiles => python_files}/testlauncher.py | 0 .../tests/__init__.py | 0 .../tests/__main__.py | 0 .../tests/debug_adapter/__init__.py | 0 .../debug_adapter/test_install_debugpy.py | 0 .../nested_folder_one/test_bottom_folder.py | 0 .../test_top_folder.py | 0 .../pytestadapter/.data/empty_discovery.py | 0 .../.data/error_parametrize_discovery.py | 0 .../.data/error_pytest_import.txt | 0 .../.data/error_raise_exception.py | 0 .../.data/error_syntax_discovery.txt | 0 .../folder_a/folder_b/folder_a/test_nest.py | 0 .../.data/param_same_name/test_param1.py | 0 .../.data/param_same_name/test_param2.py | 0 .../pytestadapter/.data/parametrize_tests.py | 0 .../pytestadapter/.data/root/tests/pytest.ini | 0 .../pytestadapter/.data/root/tests/test_a.py | 0 .../pytestadapter/.data/root/tests/test_b.py | 0 .../pytestadapter/.data/simple_pytest.py | 0 .../tests/pytestadapter/.data/skip_tests.py | 0 .../pytestadapter/.data/test_env_vars.py | 0 .../tests/pytestadapter/.data/test_logging.py | 0 .../.data/test_multi_class_nest.py | 0 .../pytestadapter/.data/text_docstring.txt | 0 .../.data/unittest_folder/test_add.py | 0 .../.data/unittest_folder/test_subtract.py | 0 .../.data/unittest_pytest_same_file.py | 0 .../.data/unittest_skiptest_file_level.py | 0 .../tests/pytestadapter/__init__.py | 0 .../expected_discovery_test_output.py | 0 .../expected_execution_test_output.py | 0 .../tests/pytestadapter/helpers.py | 0 .../tests/pytestadapter/test_discovery.py | 0 .../tests/pytestadapter/test_execution.py | 0 .../tests/run_all.py | 0 .../tests/test_create_conda.py | 0 .../tests/test_create_microvenv.py | 0 .../tests/test_create_venv.py | 0 .../tests/test_data/missing-deps.data | 0 .../tests/test_data/no-missing-deps.data | 0 .../test_data/pyproject-missing-deps.data | 0 .../test_data/pyproject-no-missing-deps.data | 0 .../tests/test_dynamic_cursor.py | 0 .../tests/test_installed_check.py | 0 .../tests/test_normalize_selection.py | 2 +- .../tests/test_shell_integration.py | 0 .../tests/test_smart_selection.py | 0 .../tests/testing_tools/__init__.py | 0 .../.data/NormCase/tests/A/__init__.py | 0 .../.data/NormCase/tests/A/b/C/__init__.py | 0 .../.data/NormCase/tests/A/b/C/test_Spam.py | 0 .../.data/NormCase/tests/A/b/__init__.py | 0 .../adapter/.data/NormCase/tests/__init__.py | 0 .../adapter/.data/complex/README.md | 2 +- .../adapter/.data/complex/mod.py | 0 .../adapter/.data/complex/tests/__init__.py | 0 .../adapter/.data/complex/tests/spam.py | 0 .../adapter/.data/complex/tests/test_42-43.py | 0 .../adapter/.data/complex/tests/test_42.py | 0 .../.data/complex/tests/test_doctest.py | 0 .../.data/complex/tests/test_doctest.txt | 0 .../adapter/.data/complex/tests/test_foo.py | 0 .../adapter/.data/complex/tests/test_mixed.py | 0 .../.data/complex/tests/test_pytest.py | 0 .../.data/complex/tests/test_pytest_param.py | 0 .../.data/complex/tests/test_unittest.py | 0 .../adapter/.data/complex/tests/testspam.py | 0 .../adapter/.data/complex/tests/v/__init__.py | 0 .../adapter/.data/complex/tests/v/spam.py | 0 .../.data/complex/tests/v/test_eggs.py | 0 .../adapter/.data/complex/tests/v/test_ham.py | 0 .../.data/complex/tests/v/test_spam.py | 0 .../.data/complex/tests/w/test_spam.py | 0 .../.data/complex/tests/w/test_spam_ex.py | 0 .../adapter/.data/complex/tests/x/__init__.py | 0 .../.data/complex/tests/x/y/__init__.py | 0 .../.data/complex/tests/x/y/z/__init__.py | 0 .../.data/complex/tests/x/y/z/a/__init__.py | 0 .../.data/complex/tests/x/y/z/a/test_spam.py | 0 .../.data/complex/tests/x/y/z/b/__init__.py | 0 .../.data/complex/tests/x/y/z/b/test_spam.py | 0 .../.data/complex/tests/x/y/z/test_ham.py | 0 .../adapter/.data/notests/tests/__init__.py | 0 .../adapter/.data/simple/tests/__init__.py | 0 .../adapter/.data/simple/tests/test_spam.py | 0 .../.data/syntax-error/tests/__init__.py | 0 .../.data/syntax-error/tests/test_spam.py | 0 .../tests/testing_tools/adapter/__init__.py | 0 .../testing_tools/adapter/pytest/__init__.py | 0 .../testing_tools/adapter/pytest/test_cli.py | 0 .../adapter/pytest/test_discovery.py | 0 .../testing_tools/adapter/test___main__.py | 0 .../testing_tools/adapter/test_discovery.py | 0 .../testing_tools/adapter/test_functional.py | 0 .../testing_tools/adapter/test_report.py | 0 .../tests/testing_tools/adapter/test_util.py | 0 .../tests/tree_comparison_helper.py | 0 .../unittestadapter/.data/discovery_empty.py | 0 .../.data/discovery_error/file_one.py | 0 .../.data/discovery_error/file_two.py | 0 .../unittestadapter/.data/discovery_simple.py | 0 .../unittestadapter/.data/test_fail_simple.py | 0 .../unittestadapter/.data/test_subtest.py | 0 .../unittestadapter/.data/test_two_classes.py | 0 .../.data/two_patterns/pattern_a_test.py | 0 .../.data/two_patterns/test_pattern_b.py | 0 .../.data/unittest_folder/test_add.py | 0 .../.data/unittest_folder/test_subtract.py | 0 .../.data/unittest_skip/unittest_skip_file.py | 0 .../unittest_skip/unittest_skip_function.py | 0 .../.data/utils_complex_tree/__init__.py | 0 .../test_outer_folder/__init__.py | 0 .../test_inner_folder/__init__.py | 0 .../test_utils_complex_tree.py | 0 .../.data/utils_decorated_tree.py | 0 .../.data/utils_nested_cases/file_one.py | 0 .../utils_nested_cases/folder/__init__.py | 0 .../utils_nested_cases/folder/file_two.py | 0 .../.data/utils_simple_cases.py | 0 .../.data/utils_simple_tree.py | 0 .../tests/unittestadapter/__init__.py | 0 .../tests/unittestadapter/conftest.py | 0 .../expected_discovery_test_output.py | 0 .../tests/unittestadapter/test_discovery.py | 0 .../tests/unittestadapter/test_execution.py | 0 .../tests/unittestadapter/test_utils.py | 0 {pythonFiles => python_files}/tests/util.py | 0 .../unittestadapter/__init__.py | 0 .../unittestadapter/discovery.py | 0 .../unittestadapter/execution.py | 0 .../unittestadapter/pvsc_utils.py | 0 .../visualstudio_py_testlauncher.py | 0 .../tests/logParser.py | 0 .../vscode_pytest/__init__.py | 0 .../vscode_pytest/run_pytest_script.py | 0 .../activation/jedi/languageClientFactory.ts | 2 +- src/client/activation/jedi/manager.ts | 2 +- src/client/api/types.ts | 2 +- .../process/internal/scripts/constants.ts | 2 +- .../common/process/internal/scripts/index.ts | 4 +- src/client/common/terminal/service.ts | 2 +- .../debugger/extension/adapter/factory.ts | 2 +- .../extension/adapter/remoteLaunchers.ts | 2 +- src/client/jupyter/jupyterIntegration.ts | 2 +- src/client/testing/common/debugLauncher.ts | 4 +- .../testing/testController/common/types.ts | 2 +- .../pytest/pytestDiscoveryAdapter.ts | 2 +- .../pytest/pytestExecutionAdapter.ts | 2 +- .../unittest/testDiscoveryAdapter.ts | 2 +- .../unittest/testExecutionAdapter.ts | 2 +- src/test/api.functional.test.ts | 2 +- src/test/common.ts | 2 +- .../common/terminals/service.unit.test.ts | 2 +- .../synchronousTerminalService.unit.test.ts | 2 +- .../extension/adapter/adapter.test.ts | 2 +- .../extension/adapter/factory.unit.test.ts | 2 +- .../adapter/remoteLaunchers.unit.test.ts | 2 +- src/test/initialize.ts | 2 +- .../activation/service.unit.test.ts | 6 +- .../info/interpreter.unit.test.ts | 2 +- .../formatting/autoPep8Formatted.py | 32 - .../pythonFiles/formatting/autopep8.output | 50 - src/test/pythonFiles/formatting/black.output | 59 - .../pythonFiles/formatting/blackFormatted.py | 41 - src/test/pythonFiles/formatting/dummy.ts | 4 - .../pythonFiles/formatting/fileToFormat.py | 22 - .../formatting/fileToFormatOnEnter.py | 13 - .../pythonFiles/formatting/formatWhenDirty.py | 3 - .../formatting/formatWhenDirtyResult.py | 3 - .../pythonFiles/formatting/pythonGrammar.py | 1572 ----------------- src/test/pythonFiles/formatting/yapf.output | 57 - .../pythonFiles/formatting/yapfFormatted.py | 40 - src/test/pythonFiles/linting/cwd/.pylintrc | 2 - src/test/pythonFiles/linting/file.py | 87 - .../pythonFiles/linting/flake8config/.flake8 | 2 - .../pythonFiles/linting/flake8config/file.py | 86 - src/test/pythonFiles/linting/minCheck.py | 1 - src/test/pythonFiles/linting/print.py | 1 - .../linting/pycodestyleconfig/.pycodestyle | 2 - .../linting/pycodestyleconfig/file.py | 87 - .../linting/pydocstyleconfig27/.pydocstyle | 2 - .../linting/pydocstyleconfig27/file.py | 87 - .../linting/pylintconfig/.pylintrc | 2 - .../pythonFiles/linting/pylintconfig/file.py | 87 - .../pythonFiles/linting/pylintconfig/file2.py | 19 - .../pythonFiles/linting/threeLineLints.py | 24 - .../.vscode/.ropeproject/config.py | 114 -- .../.vscode/.ropeproject/objectdb | Bin 6 -> 0 bytes .../pythonFiles/sorting/noconfig/after.py | 16 - .../pythonFiles/sorting/noconfig/before.py | 18 - .../pythonFiles/sorting/noconfig/original.py | 18 - .../pythonFiles/sorting/withconfig/.isort.cfg | 2 - .../pythonFiles/sorting/withconfig/after.py | 3 - .../sorting/withconfig/before.1.py | 3 - .../pythonFiles/sorting/withconfig/before.py | 3 - .../sorting/withconfig/original.1.py | 3 - .../sorting/withconfig/original.py | 3 - .../datascience/simple_nb.ipynb | 0 .../datascience/simple_note_book.py | 0 .../debugging/forever.py | 0 .../debugging/logMessage.py | 0 .../debugging/loopyTest.py | 0 .../debugging/multiThread.py | 0 .../debugging/printSysArgv.py | 0 .../debugging/sample2.py | 0 .../debugging/sample2WithoutSleep.py | 0 .../debugging/sample3WithEx.py | 0 .../debugging/sampleWithAssertEx.py | 0 .../debugging/sampleWithSleep.py | 0 .../debugging/simplePrint.py | 0 .../debugging/stackFrame.py | 0 .../debugging/startAndWait.py | 0 .../debugging/stdErrOutput.py | 0 .../debugging/stdOutOutput.py | 0 .../debugging/wait_for_file.py | 0 .../{pythonFiles => python_files}/dummy.py | 0 .../environments/conda/Scripts/conda.exe | 0 .../environments/conda/bin/python | 0 .../environments/conda/envs/numpy/bin/python | 0 .../environments/conda/envs/numpy/python.exe | 0 .../environments/conda/envs/scipy/bin/python | 0 .../environments/conda/envs/scipy/python.exe | 0 .../environments/path1/one | 0 .../environments/path1/one.exe | 0 .../environments/path1/python.exe | 0 .../environments/path2/one | 0 .../environments/path2/one.exe | 0 .../environments/path2/python.exe | 0 .../intellisense/test.py | 0 .../shebang/plain.py | 0 .../shebang/shebang.py | 0 .../shebang/shebangEnv.py | 0 .../shebang/shebangInvalid.py | 0 .../tensorBoard/noMatch.py | 0 .../tensorBoard/sourcefile.py | 0 .../tensorBoard/tensorboard_import.ipynb | 0 .../tensorBoard/tensorboard_imports.py | 0 .../tensorBoard/tensorboard_launch.py | 0 .../tensorBoard/tensorboard_nbextension.ipynb | 0 .../terminalExec/sample1_normalized.py | 0 .../sample1_normalized_selection.py | 0 .../terminalExec/sample1_raw.py | 0 .../terminalExec/sample2_normalized.py | 0 .../sample2_normalized_selection.py | 0 .../terminalExec/sample2_raw.py | 0 .../terminalExec/sample3_normalized.py | 0 .../sample3_normalized_selection.py | 0 .../terminalExec/sample3_raw.py | 0 .../terminalExec/sample4_normalized.py | 0 .../sample4_normalized_selection.py | 0 .../terminalExec/sample4_raw.py | 0 .../terminalExec/sample5_normalized.py | 0 .../sample5_normalized_selection.py | 0 .../terminalExec/sample5_raw.py | 0 .../terminalExec/sample6_normalized.py | 0 .../sample6_normalized_selection.py | 0 .../terminalExec/sample6_raw.py | 0 .../terminalExec/sample7_normalized.py | 0 .../sample7_normalized_selection.py | 0 .../terminalExec/sample7_raw.py | 0 .../terminalExec/sample8_normalized.py | 0 .../sample8_normalized_selection.py | 0 .../terminalExec/sample8_raw.py | 0 .../sample_invalid_smart_selection.py | 0 .../terminalExec/sample_normalized.py | 0 .../sample_normalized_selection.py | 0 .../terminalExec/sample_raw.py | 0 .../terminalExec/sample_smart_selection.py | 0 src/test/smoke/datascience.smoke.test.ts | 4 +- src/test/smoke/jedilsp.smoke.test.ts | 2 +- .../tensorBoard/tensorBoardSession.test.ts | 2 +- .../terminals/codeExecution/helper.test.ts | 2 +- .../terminals/codeExecution/smartSend.test.ts | 2 +- .../testing/common/debugLauncher.unit.test.ts | 10 +- .../pytestDiscoveryAdapter.unit.test.ts | 2 +- .../pytestExecutionAdapter.unit.test.ts | 4 +- .../testDiscoveryAdapter.unit.test.ts | 4 +- .../testExecutionAdapter.unit.test.ts | 4 +- 337 files changed, 140 insertions(+), 2704 deletions(-) rename {pythonFiles => python_files}/.env (100%) rename {pythonFiles => python_files}/.vscode/settings.json (100%) rename {pythonFiles => python_files}/Notebooks intro.ipynb (100%) rename {pythonFiles => python_files}/create_conda.py (100%) rename {pythonFiles => python_files}/create_microvenv.py (100%) rename {pythonFiles => python_files}/create_venv.py (100%) rename {pythonFiles => python_files}/deactivate/bash/deactivate (100%) mode change 100755 => 100644 rename {pythonFiles => python_files}/deactivate/fish/deactivate (100%) mode change 100755 => 100644 rename {pythonFiles => python_files}/deactivate/powershell/deactivate.ps1 (100%) rename {pythonFiles => python_files}/deactivate/zsh/deactivate (100%) mode change 100755 => 100644 rename {pythonFiles => python_files}/download_get_pip.py (96%) rename {pythonFiles => python_files}/get_output_via_markers.py (100%) rename {pythonFiles => python_files}/install_debugpy.py (96%) rename {pythonFiles => python_files}/installed_check.py (100%) rename {pythonFiles => python_files}/interpreterInfo.py (100%) rename {pythonFiles => python_files}/jedilsp_requirements/requirements.in (72%) rename {pythonFiles => python_files}/jedilsp_requirements/requirements.txt (93%) rename {pythonFiles => python_files}/linter.py (100%) rename {pythonFiles => python_files}/normalizeSelection.py (100%) rename {pythonFiles => python_files}/printEnvVariables.py (100%) rename {pythonFiles => python_files}/printEnvVariablesToFile.py (100%) rename {pythonFiles => python_files}/pyproject.toml (86%) rename {pythonFiles => python_files}/pythonrc.py (100%) rename {pythonFiles => python_files}/run-jedi-language-server.py (74%) rename {pythonFiles => python_files}/shell_exec.py (100%) rename {pythonFiles => python_files}/tensorboard_launcher.py (100%) rename {pythonFiles => python_files}/testing_tools/__init__.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/__init__.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/__main__.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/discovery.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/errors.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/info.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/pytest/__init__.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/pytest/_cli.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/pytest/_discovery.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/pytest/_pytest_item.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/report.py (100%) rename {pythonFiles => python_files}/testing_tools/adapter/util.py (100%) rename {pythonFiles => python_files}/testing_tools/process_json_util.py (100%) rename {pythonFiles => python_files}/testing_tools/run_adapter.py (81%) rename {pythonFiles => python_files}/testing_tools/socket_manager.py (100%) rename {pythonFiles => python_files}/testing_tools/unittest_discovery.py (100%) rename {pythonFiles => python_files}/testlauncher.py (100%) rename {pythonFiles => python_files}/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/__main__.py (100%) rename {pythonFiles => python_files}/tests/debug_adapter/__init__.py (100%) rename {pythonFiles => python_files}/tests/debug_adapter/test_install_debugpy.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/dual_level_nested_folder/test_top_folder.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/empty_discovery.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/error_parametrize_discovery.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/error_pytest_import.txt (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/error_raise_exception.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/error_syntax_discovery.txt (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/folder_a/folder_b/folder_a/test_nest.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/param_same_name/test_param1.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/param_same_name/test_param2.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/parametrize_tests.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/root/tests/pytest.ini (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/root/tests/test_a.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/root/tests/test_b.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/simple_pytest.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/skip_tests.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/test_env_vars.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/test_logging.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/test_multi_class_nest.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/text_docstring.txt (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/unittest_folder/test_add.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/unittest_folder/test_subtract.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/unittest_pytest_same_file.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/.data/unittest_skiptest_file_level.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/__init__.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/expected_discovery_test_output.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/expected_execution_test_output.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/helpers.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/test_discovery.py (100%) rename {pythonFiles => python_files}/tests/pytestadapter/test_execution.py (100%) rename {pythonFiles => python_files}/tests/run_all.py (100%) rename {pythonFiles => python_files}/tests/test_create_conda.py (100%) rename {pythonFiles => python_files}/tests/test_create_microvenv.py (100%) rename {pythonFiles => python_files}/tests/test_create_venv.py (100%) rename {pythonFiles => python_files}/tests/test_data/missing-deps.data (100%) rename {pythonFiles => python_files}/tests/test_data/no-missing-deps.data (100%) rename {pythonFiles => python_files}/tests/test_data/pyproject-missing-deps.data (100%) rename {pythonFiles => python_files}/tests/test_data/pyproject-no-missing-deps.data (100%) rename {pythonFiles => python_files}/tests/test_dynamic_cursor.py (100%) rename {pythonFiles => python_files}/tests/test_installed_check.py (100%) rename {pythonFiles => python_files}/tests/test_normalize_selection.py (98%) rename {pythonFiles => python_files}/tests/test_shell_integration.py (100%) rename {pythonFiles => python_files}/tests/test_smart_selection.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/NormCase/tests/A/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/test_Spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/NormCase/tests/A/b/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/NormCase/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/README.md (99%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/mod.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_42-43.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_42.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_doctest.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_doctest.txt (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_foo.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_mixed.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_pytest.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_pytest_param.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/test_unittest.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/testspam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/v/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/v/spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/v/test_eggs.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/v/test_ham.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/v/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/w/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/w/test_spam_ex.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/complex/tests/x/y/z/test_ham.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/notests/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/simple/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/simple/tests/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/syntax-error/tests/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/.data/syntax-error/tests/test_spam.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/pytest/__init__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/pytest/test_cli.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/pytest/test_discovery.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/test___main__.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/test_discovery.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/test_functional.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/test_report.py (100%) rename {pythonFiles => python_files}/tests/testing_tools/adapter/test_util.py (100%) rename {pythonFiles => python_files}/tests/tree_comparison_helper.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/discovery_empty.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/discovery_error/file_one.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/discovery_error/file_two.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/discovery_simple.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/test_fail_simple.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/test_subtest.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/test_two_classes.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/two_patterns/pattern_a_test.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/two_patterns/test_pattern_b.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/unittest_folder/test_add.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/unittest_folder/test_subtract.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/unittest_skip/unittest_skip_file.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/unittest_skip/unittest_skip_function.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_complex_tree/__init__.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_decorated_tree.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_nested_cases/file_one.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_nested_cases/folder/__init__.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_nested_cases/folder/file_two.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_simple_cases.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/.data/utils_simple_tree.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/__init__.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/conftest.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/expected_discovery_test_output.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/test_discovery.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/test_execution.py (100%) rename {pythonFiles => python_files}/tests/unittestadapter/test_utils.py (100%) rename {pythonFiles => python_files}/tests/util.py (100%) rename {pythonFiles => python_files}/unittestadapter/__init__.py (100%) rename {pythonFiles => python_files}/unittestadapter/discovery.py (100%) rename {pythonFiles => python_files}/unittestadapter/execution.py (100%) rename {pythonFiles => python_files}/unittestadapter/pvsc_utils.py (100%) rename {pythonFiles => python_files}/visualstudio_py_testlauncher.py (100%) rename {pythonFiles => python_files}/vscode_datascience_helpers/tests/logParser.py (100%) rename {pythonFiles => python_files}/vscode_pytest/__init__.py (100%) rename {pythonFiles => python_files}/vscode_pytest/run_pytest_script.py (100%) delete mode 100644 src/test/pythonFiles/formatting/autoPep8Formatted.py delete mode 100644 src/test/pythonFiles/formatting/autopep8.output delete mode 100644 src/test/pythonFiles/formatting/black.output delete mode 100644 src/test/pythonFiles/formatting/blackFormatted.py delete mode 100644 src/test/pythonFiles/formatting/dummy.ts delete mode 100644 src/test/pythonFiles/formatting/fileToFormat.py delete mode 100644 src/test/pythonFiles/formatting/fileToFormatOnEnter.py delete mode 100644 src/test/pythonFiles/formatting/formatWhenDirty.py delete mode 100644 src/test/pythonFiles/formatting/formatWhenDirtyResult.py delete mode 100644 src/test/pythonFiles/formatting/pythonGrammar.py delete mode 100644 src/test/pythonFiles/formatting/yapf.output delete mode 100644 src/test/pythonFiles/formatting/yapfFormatted.py delete mode 100644 src/test/pythonFiles/linting/cwd/.pylintrc delete mode 100644 src/test/pythonFiles/linting/file.py delete mode 100644 src/test/pythonFiles/linting/flake8config/.flake8 delete mode 100644 src/test/pythonFiles/linting/flake8config/file.py delete mode 100644 src/test/pythonFiles/linting/minCheck.py delete mode 100644 src/test/pythonFiles/linting/print.py delete mode 100644 src/test/pythonFiles/linting/pycodestyleconfig/.pycodestyle delete mode 100644 src/test/pythonFiles/linting/pycodestyleconfig/file.py delete mode 100644 src/test/pythonFiles/linting/pydocstyleconfig27/.pydocstyle delete mode 100644 src/test/pythonFiles/linting/pydocstyleconfig27/file.py delete mode 100644 src/test/pythonFiles/linting/pylintconfig/.pylintrc delete mode 100644 src/test/pythonFiles/linting/pylintconfig/file.py delete mode 100644 src/test/pythonFiles/linting/pylintconfig/file2.py delete mode 100644 src/test/pythonFiles/linting/threeLineLints.py delete mode 100644 src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/config.py delete mode 100644 src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/objectdb delete mode 100644 src/test/pythonFiles/sorting/noconfig/after.py delete mode 100644 src/test/pythonFiles/sorting/noconfig/before.py delete mode 100644 src/test/pythonFiles/sorting/noconfig/original.py delete mode 100644 src/test/pythonFiles/sorting/withconfig/.isort.cfg delete mode 100644 src/test/pythonFiles/sorting/withconfig/after.py delete mode 100644 src/test/pythonFiles/sorting/withconfig/before.1.py delete mode 100644 src/test/pythonFiles/sorting/withconfig/before.py delete mode 100644 src/test/pythonFiles/sorting/withconfig/original.1.py delete mode 100644 src/test/pythonFiles/sorting/withconfig/original.py rename src/test/{pythonFiles => python_files}/datascience/simple_nb.ipynb (100%) rename src/test/{pythonFiles => python_files}/datascience/simple_note_book.py (100%) rename src/test/{pythonFiles => python_files}/debugging/forever.py (100%) rename src/test/{pythonFiles => python_files}/debugging/logMessage.py (100%) rename src/test/{pythonFiles => python_files}/debugging/loopyTest.py (100%) rename src/test/{pythonFiles => python_files}/debugging/multiThread.py (100%) rename src/test/{pythonFiles => python_files}/debugging/printSysArgv.py (100%) rename src/test/{pythonFiles => python_files}/debugging/sample2.py (100%) rename src/test/{pythonFiles => python_files}/debugging/sample2WithoutSleep.py (100%) rename src/test/{pythonFiles => python_files}/debugging/sample3WithEx.py (100%) rename src/test/{pythonFiles => python_files}/debugging/sampleWithAssertEx.py (100%) rename src/test/{pythonFiles => python_files}/debugging/sampleWithSleep.py (100%) rename src/test/{pythonFiles => python_files}/debugging/simplePrint.py (100%) rename src/test/{pythonFiles => python_files}/debugging/stackFrame.py (100%) rename src/test/{pythonFiles => python_files}/debugging/startAndWait.py (100%) rename src/test/{pythonFiles => python_files}/debugging/stdErrOutput.py (100%) rename src/test/{pythonFiles => python_files}/debugging/stdOutOutput.py (100%) rename src/test/{pythonFiles => python_files}/debugging/wait_for_file.py (100%) rename src/test/{pythonFiles => python_files}/dummy.py (100%) rename src/test/{pythonFiles => python_files}/environments/conda/Scripts/conda.exe (100%) rename src/test/{pythonFiles => python_files}/environments/conda/bin/python (100%) rename src/test/{pythonFiles => python_files}/environments/conda/envs/numpy/bin/python (100%) rename src/test/{pythonFiles => python_files}/environments/conda/envs/numpy/python.exe (100%) rename src/test/{pythonFiles => python_files}/environments/conda/envs/scipy/bin/python (100%) rename src/test/{pythonFiles => python_files}/environments/conda/envs/scipy/python.exe (100%) rename src/test/{pythonFiles => python_files}/environments/path1/one (100%) rename src/test/{pythonFiles => python_files}/environments/path1/one.exe (100%) rename src/test/{pythonFiles => python_files}/environments/path1/python.exe (100%) rename src/test/{pythonFiles => python_files}/environments/path2/one (100%) rename src/test/{pythonFiles => python_files}/environments/path2/one.exe (100%) rename src/test/{pythonFiles => python_files}/environments/path2/python.exe (100%) rename src/test/{pythonFiles => python_files}/intellisense/test.py (100%) rename src/test/{pythonFiles => python_files}/shebang/plain.py (100%) rename src/test/{pythonFiles => python_files}/shebang/shebang.py (100%) rename src/test/{pythonFiles => python_files}/shebang/shebangEnv.py (100%) rename src/test/{pythonFiles => python_files}/shebang/shebangInvalid.py (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/noMatch.py (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/sourcefile.py (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/tensorboard_import.ipynb (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/tensorboard_imports.py (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/tensorboard_launch.py (100%) rename src/test/{pythonFiles => python_files}/tensorBoard/tensorboard_nbextension.ipynb (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample1_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample1_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample1_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample2_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample2_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample2_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample3_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample3_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample3_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample4_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample4_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample4_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample5_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample5_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample5_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample6_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample6_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample6_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample7_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample7_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample7_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample8_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample8_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample8_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample_invalid_smart_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample_normalized.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample_normalized_selection.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample_raw.py (100%) rename src/test/{pythonFiles => python_files}/terminalExec/sample_smart_selection.py (100%) diff --git a/.eslintignore b/.eslintignore index 7f6bb48d6c8e..9399ff461dcd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -105,7 +105,7 @@ src/test/common/process/proc.unit.test.ts src/test/common/interpreterPathService.unit.test.ts -src/test/pythonFiles/formatting/dummy.ts +src/test/python_files/formatting/dummy.ts src/test/debugger/extension/adapter/adapter.test.ts src/test/debugger/extension/adapter/outdatedDebuggerPrompt.unit.test.ts diff --git a/.github/actions/build-vsix/action.yml b/.github/actions/build-vsix/action.yml index 52d6d1cdbdd4..5b8a569889a8 100644 --- a/.github/actions/build-vsix/action.yml +++ b/.github/actions/build-vsix/action.yml @@ -30,7 +30,7 @@ runs: cache-dependency-path: | requirements.txt build/build-install-requirements.txt - pythonFiles/jedilsp_requirements/requirements.txt + python_files/jedilsp_requirements/requirements.txt - name: Upgrade Pip run: python -m pip install -U pip @@ -44,20 +44,20 @@ runs: - name: Install Python dependencies uses: brettcannon/pip-secure-install@v1 with: - options: '-t ./pythonFiles/lib/python --implementation py' + options: '-t ./python_files/lib/python --implementation py' - name: Install debugpy and get-pip run: | python -m pip --disable-pip-version-check install packaging - python ./pythonFiles/install_debugpy.py - python ./pythonFiles/download_get_pip.py + python ./python_files/install_debugpy.py + python ./python_files/download_get_pip.py shell: bash - name: Install Jedi LSP uses: brettcannon/pip-secure-install@v1 with: - requirements-file: './pythonFiles/jedilsp_requirements/requirements.txt' - options: '-t ./pythonFiles/lib/jedilsp --implementation py --platform any --abi none' + requirements-file: './python_files/jedilsp_requirements/requirements.txt' + options: '-t ./python_files/lib/jedilsp --implementation py --platform any --abi none' - name: Run npm ci run: npm ci --prefer-offline diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index 47924c108151..ed0e380bed6e 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -45,12 +45,12 @@ runs: run: | python -m pip install -U black python -m black . --check - working-directory: pythonFiles + working-directory: python_files shell: bash - name: Run Ruff run: | python -m pip install -U ruff python -m ruff check . - working-directory: pythonFiles + working-directory: python_files shell: bash diff --git a/.github/actions/smoke-tests/action.yml b/.github/actions/smoke-tests/action.yml index b2d002050433..7eaa2483942a 100644 --- a/.github/actions/smoke-tests/action.yml +++ b/.github/actions/smoke-tests/action.yml @@ -34,12 +34,12 @@ runs: - name: Install Python requirements uses: brettcannon/pip-secure-install@v1 with: - options: '-t ./pythonFiles/lib/python --implementation py' + options: '-t ./python_files/lib/python --implementation py' - name: pip install system test requirements run: | python -m pip install --upgrade -r build/test-requirements.txt - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --implementation py --no-deps --upgrade --pre debugpy shell: bash # Bits from the VSIX are reused by smokeTest.ts to speed things up. diff --git a/.github/release_plan.md b/.github/release_plan.md index f1d95d8577d0..8c7e1e660d36 100644 --- a/.github/release_plan.md +++ b/.github/release_plan.md @@ -37,7 +37,7 @@ NOTE: the number of this release is in the issue title and can be substituted in - [ ] Create a new branch called **`bump-release-[YYYY.minor]`**. - [ ] Change the version in `package.json` to the next **even** number and switch the `-dev` to `-rc`. (🤖) - [ ] Run `npm install` to make sure `package-lock.json` is up-to-date _(you should now see changes to the `package.json` and `package-lock.json` at this point which update the version number **only**)_. (🤖) -- [ ] Check [debugpy on PyPI](https://pypi.org/project/debugpy/) for a new release and update the version of debugpy in [`install_debugpy.py`](https://github.com/microsoft/vscode-python/blob/main/pythonFiles/install_debugpy.py) if necessary. +- [ ] Check [debugpy on PyPI](https://pypi.org/project/debugpy/) for a new release and update the version of debugpy in [`install_debugpy.py`](https://github.com/microsoft/vscode-python/blob/main/python_files/install_debugpy.py) if necessary. - [ ] Update `ThirdPartyNotices-Repository.txt` as appropriate. You can check by looking at the [commit history](https://github.com/microsoft/vscode-python/commits/main) and scrolling through to see if there's anything listed there which might have pulled in some code directly into the repository from somewhere else. If you are still unsure you can check with the team. - [ ] Create a PR from your branch **`bump-release-[YYYY.minor]`** to `main`. Add the `"no change-log"` tag to the PR so it does not show up on the release notes before merging it. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3de5bd1ff756..1791e9797fa7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,24 +87,24 @@ jobs: - name: Install core Python requirements uses: brettcannon/pip-secure-install@v1 with: - options: '-t ./pythonFiles/lib/python --no-cache-dir --implementation py' + options: '-t ./python_files/lib/python --no-cache-dir --implementation py' - name: Install Jedi requirements uses: brettcannon/pip-secure-install@v1 with: - requirements-file: './pythonFiles/jedilsp_requirements/requirements.txt' - options: '-t ./pythonFiles/lib/jedilsp --no-cache-dir --implementation py' + requirements-file: './python_files/jedilsp_requirements/requirements.txt' + options: '-t ./python_files/lib/jedilsp --no-cache-dir --implementation py' - name: Install other Python requirements run: | - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy python -m pip install --upgrade -r build/test-requirements.txt - name: Run Pyright uses: jakebailey/pyright-action@v2 with: version: 1.1.308 - working-directory: 'pythonFiles' + working-directory: 'python_files' python-tests: name: Python Tests @@ -137,13 +137,13 @@ jobs: uses: brettcannon/pip-secure-install@v1 with: requirements-file: '"${{ env.special-working-directory-relative }}/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/python" --no-cache-dir --implementation py' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/python" --no-cache-dir --implementation py' - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt - name: Run Python unit tests - run: python pythonFiles/tests/run_all.py + run: python python_files/tests/run_all.py tests: name: Tests @@ -192,26 +192,26 @@ jobs: run: | python -m pip install wheel python -m pip install -r build/build-install-requirements.txt - python ./pythonFiles/download_get_pip.py + python ./python_files/download_get_pip.py shell: bash - name: Install debugpy run: | # We need to have debugpy so that tests relying on it keep passing, but we don't need install_debugpy's logic in the test phase. - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy - name: Install core Python requirements uses: brettcannon/pip-secure-install@v1 with: requirements-file: '"${{ env.special-working-directory-relative }}/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/python" --no-cache-dir --implementation py' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/python" --no-cache-dir --implementation py' if: startsWith(matrix.python, 3.) - name: Install Jedi requirements uses: brettcannon/pip-secure-install@v1 with: - requirements-file: '"${{ env.special-working-directory-relative }}/pythonFiles/jedilsp_requirements/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/jedilsp" --no-cache-dir --implementation py' + requirements-file: '"${{ env.special-working-directory-relative }}/python_files/jedilsp_requirements/requirements.txt"' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/jedilsp" --no-cache-dir --implementation py' if: startsWith(matrix.python, 3.) - name: Install test requirements @@ -221,7 +221,7 @@ jobs: run: | python -m pip install wheel python -m pip install -r build/build-install-requirements.txt - python ./pythonFiles/install_debugpy.py + python ./python_files/install_debugpy.py shell: bash if: matrix.test-suite == 'debugger' diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 5ec7027c74e5..46c061d3cc1c 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -61,24 +61,24 @@ jobs: - name: Install base Python requirements uses: brettcannon/pip-secure-install@v1 with: - options: '-t ./pythonFiles/lib/python --no-cache-dir --implementation py' + options: '-t ./python_files/lib/python --no-cache-dir --implementation py' - name: Install Jedi requirements uses: brettcannon/pip-secure-install@v1 with: - requirements-file: './pythonFiles/jedilsp_requirements/requirements.txt' - options: '-t ./pythonFiles/lib/jedilsp --no-cache-dir --implementation py' + requirements-file: './python_files/jedilsp_requirements/requirements.txt' + options: '-t ./python_files/lib/jedilsp --no-cache-dir --implementation py' - name: Install other Python requirements run: | - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy python -m pip install --upgrade -r build/test-requirements.txt - name: Run Pyright uses: jakebailey/pyright-action@v2 with: version: 1.1.308 - working-directory: 'pythonFiles' + working-directory: 'python_files' python-tests: name: Python Tests @@ -124,13 +124,13 @@ jobs: uses: brettcannon/pip-secure-install@v1 with: requirements-file: '"${{ env.special-working-directory-relative }}/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/python" --no-cache-dir --implementation py' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/python" --no-cache-dir --implementation py' - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt - name: Run Python unit tests - run: python pythonFiles/tests/run_all.py + run: python python_files/tests/run_all.py tests: name: Tests @@ -179,26 +179,26 @@ jobs: - name: Install debugpy run: | # We need to have debugpy so that tests relying on it keep passing, but we don't need install_debugpy's logic in the test phase. - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --no-cache-dir --implementation py --no-deps --upgrade --pre debugpy - name: Download get-pip.py run: | python -m pip install wheel python -m pip install -r build/build-install-requirements.txt - python ./pythonFiles/download_get_pip.py + python ./python_files/download_get_pip.py shell: bash - name: Install base Python requirements uses: brettcannon/pip-secure-install@v1 with: requirements-file: '"${{ env.special-working-directory-relative }}/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/python" --no-cache-dir --implementation py' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/python" --no-cache-dir --implementation py' - name: Install Jedi requirements uses: brettcannon/pip-secure-install@v1 with: - requirements-file: '"${{ env.special-working-directory-relative }}/pythonFiles/jedilsp_requirements/requirements.txt"' - options: '-t "${{ env.special-working-directory-relative }}/pythonFiles/lib/jedilsp" --no-cache-dir --implementation py' + requirements-file: '"${{ env.special-working-directory-relative }}/python_files/jedilsp_requirements/requirements.txt"' + options: '-t "${{ env.special-working-directory-relative }}/python_files/lib/jedilsp" --no-cache-dir --implementation py' - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt @@ -207,7 +207,7 @@ jobs: run: | python -m pip install wheel python -m pip --disable-pip-version-check install -r build/build-install-requirements.txt - python ./pythonFiles/install_debugpy.py + python ./python_files/install_debugpy.py shell: bash if: matrix.test-suite == 'debugger' @@ -393,25 +393,25 @@ jobs: cache: 'pip' cache-dependency-path: | requirements.txt - pythonFiles/jedilsp_requirements/requirements.txt + python_files/jedilsp_requirements/requirements.txt build/test-requirements.txt build/functional-test-requirements.txt - name: Install base Python requirements uses: brettcannon/pip-secure-install@v1 with: - options: '-t ./pythonFiles/lib/python --implementation py' + options: '-t ./python_files/lib/python --implementation py' - name: Install Jedi requirements uses: brettcannon/pip-secure-install@v1 with: - requirements-file: './pythonFiles/jedilsp_requirements/requirements.txt' - options: '-t ./pythonFiles/lib/jedilsp --implementation py' + requirements-file: './python_files/jedilsp_requirements/requirements.txt' + options: '-t ./python_files/lib/jedilsp --implementation py' - name: Install debugpy run: | # We need to have debugpy so that tests relying on it keep passing, but we don't need install_debugpy's logic in the test phase. - python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --implementation py --no-deps --upgrade --pre debugpy + python -m pip --disable-pip-version-check install -t ./python_files/lib/python --implementation py --no-deps --upgrade --pre debugpy - name: Install test requirements run: python -m pip install --upgrade -r build/test-requirements.txt @@ -487,7 +487,7 @@ jobs: - name: Run Python unit tests run: | - python pythonFiles/tests/run_all.py + python python_files/tests/run_all.py # The virtual environment based tests use the `testSingleWorkspace` set of tests # with the environment variable `TEST_FILES_SUFFIX` set to `testvirtualenvs`, diff --git a/.gitignore b/.gitignore index 69166ccac1a4..0556cbb2df0e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,8 @@ cucumber-report.json **/.venv*/ port.txt precommit.hook -pythonFiles/lib/** -pythonFiles/get-pip.py +python_files/lib/** +python_files/get-pip.py debug_coverage*/** languageServer/** languageServer.*/** diff --git a/.vscode/launch.json b/.vscode/launch.json index 82981a93305d..152ef1c87f60 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -249,7 +249,7 @@ }, { "name": "Listen", - "type": "python", + "type": "debugpy", "request": "attach", "listen": { "host": "localhost", "port": 5678 }, "justMyCode": true @@ -257,10 +257,10 @@ { "name": "Debug pytest plugin tests", - "type": "python", + "type": "debugpy", "request": "launch", "module": "pytest", - "args": ["${workspaceFolder}/pythonFiles/tests/pytestadapter"], + "args": ["${workspaceFolder}/python_files/tests/pytestadapter"], "justMyCode": true } ], diff --git a/.vscode/settings.json b/.vscode/settings.json index 50864f0b5cd8..5e5954991562 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,7 +64,7 @@ // Open merge editor for resolving conflicts. "git.mergeEditor": true, "python.testing.pytestArgs": [ - "pythonFiles/tests" + "python_files/tests" ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true diff --git a/.vscodeignore b/.vscodeignore index 6788f9b6d8e1..9d1934545bf1 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -51,16 +51,16 @@ obj/** out/**/*.stats.json out/client/**/*.analyzer.html out/coverconfig.json -out/pythonFiles/** +out/python_files/** out/src/** out/test/** out/testMultiRootWkspc/** precommit.hook -pythonFiles/**/*.pyc -pythonFiles/lib/**/*.egg-info/** -pythonFiles/lib/python/bin/** -pythonFiles/jedilsp_requirements/** -pythonFiles/tests/** +python_files/**/*.pyc +python_files/lib/**/*.egg-info/** +python_files/lib/python/bin/** +python_files/jedilsp_requirements/** +python_files/tests/** scripts/** src/** test/** diff --git a/build/azure-pipeline.pre-release.yml b/build/azure-pipeline.pre-release.yml index bb52f983d02e..5f66ae71cfec 100644 --- a/build/azure-pipeline.pre-release.yml +++ b/build/azure-pipeline.pre-release.yml @@ -54,16 +54,16 @@ extends: - script: | python -m pip --disable-pip-version-check install -r build/build-install-requirements.txt - python ./pythonFiles/install_debugpy.py - python ./pythonFiles/download_get_pip.py + python ./python_files/install_debugpy.py + python ./python_files/download_get_pip.py displayName: Install debugpy and get-pip.py - script: | - python -m pip install --no-deps --require-hashes --only-binary :all: -t ./pythonFiles/lib/python --implementation py -r ./requirements.txt + python -m pip install --no-deps --require-hashes --only-binary :all: -t ./python_files/lib/python --implementation py -r ./requirements.txt displayName: Install Python dependencies - script: | - python -m pip install --no-deps --require-hashes --only-binary :all: -t ./pythonFiles/lib/jedilsp --implementation py --platform any --abi none -r ./pythonFiles/jedilsp_requirements/requirements.txt + python -m pip install --no-deps --require-hashes --only-binary :all: -t ./python_files/lib/jedilsp --implementation py --platform any --abi none -r ./python_files/jedilsp_requirements/requirements.txt displayName: Install Jedi Language Server - script: | diff --git a/build/azure-pipeline.stable.yml b/build/azure-pipeline.stable.yml index 8d04ebab318a..2aeafbc1e0df 100644 --- a/build/azure-pipeline.stable.yml +++ b/build/azure-pipeline.stable.yml @@ -49,16 +49,16 @@ extends: - script: | python -m pip --disable-pip-version-check install -r build/build-install-requirements.txt - python ./pythonFiles/install_debugpy.py - python ./pythonFiles/download_get_pip.py + python ./python_files/install_debugpy.py + python ./python_files/download_get_pip.py displayName: Install debugpy and get-pip.py - script: | - python -m pip install --no-deps --require-hashes --only-binary :all: -t ./pythonFiles/lib/python --implementation py -r ./requirements.txt + python -m pip install --no-deps --require-hashes --only-binary :all: -t ./python_files/lib/python --implementation py -r ./requirements.txt displayName: Install Python dependencies - script: | - python -m pip install --no-deps --require-hashes --only-binary :all: -t ./pythonFiles/lib/jedilsp --implementation py --platform any --abi none -r ./pythonFiles/jedilsp_requirements/requirements.txt + python -m pip install --no-deps --require-hashes --only-binary :all: -t ./python_files/lib/jedilsp --implementation py --platform any --abi none -r ./python_files/jedilsp_requirements/requirements.txt displayName: Install Jedi Language Server - script: | diff --git a/build/existingFiles.json b/build/existingFiles.json index 1f5acc727d8e..48ab84ff565d 100644 --- a/build/existingFiles.json +++ b/build/existingFiles.json @@ -500,7 +500,7 @@ "src/test/providers/shebangCodeLenseProvider.test.ts", "src/test/providers/symbolProvider.unit.test.ts", "src/test/providers/terminal.unit.test.ts", - "src/test/pythonFiles/formatting/dummy.ts", + "src/test/python_files/formatting/dummy.ts", "src/test/refactor/extension.refactor.extract.method.test.ts", "src/test/refactor/extension.refactor.extract.var.test.ts", "src/test/refactor/rename.test.ts", diff --git a/noxfile.py b/noxfile.py index b9ebba64544a..aa6d0253c660 100644 --- a/noxfile.py +++ b/noxfile.py @@ -9,10 +9,10 @@ @nox.session() def install_python_libs(session: nox.Session): requirements = [ - ("./pythonFiles/lib/python", "./requirements.txt"), + ("./python_files/lib/python", "./requirements.txt"), ( - "./pythonFiles/lib/jedilsp", - "./pythonFiles/jedilsp_requirements/requirements.txt", + "./python_files/lib/jedilsp", + "./python_files/jedilsp_requirements/requirements.txt", ), ] for target, file in requirements: @@ -35,16 +35,16 @@ def install_python_libs(session: nox.Session): # Install debugger session.run( "python", - "./pythonFiles/install_debugpy.py", - env={"PYTHONPATH": "./pythonFiles/lib/temp"}, + "./python_files/install_debugpy.py", + env={"PYTHONPATH": "./python_files/lib/temp"}, ) # Download get-pip script session.run( "python", - "./pythonFiles/download_get_pip.py", - env={"PYTHONPATH": "./pythonFiles/lib/temp"}, + "./python_files/download_get_pip.py", + env={"PYTHONPATH": "./python_files/lib/temp"}, ) - if pathlib.Path("./pythonFiles/lib/temp").exists(): - shutil.rmtree("./pythonFiles/lib/temp") + if pathlib.Path("./python_files/lib/temp").exists(): + shutil.rmtree("./python_files/lib/temp") diff --git a/pythonExtensionApi/src/main.ts b/pythonExtensionApi/src/main.ts index 4de554bf5a24..dccbd78f6f04 100644 --- a/pythonExtensionApi/src/main.ts +++ b/pythonExtensionApi/src/main.ts @@ -16,7 +16,7 @@ export interface PythonExtension { /** * Generate an array of strings for commands to pass to the Python executable to launch the debugger for remote debugging. * Users can append another array of strings of what they want to execute along with relevant arguments to Python. - * E.g `['/Users/..../pythonVSCode/pythonFiles/lib/python/debugpy', '--listen', 'localhost:57039', '--wait-for-client']` + * E.g `['/Users/..../pythonVSCode/python_files/lib/python/debugpy', '--listen', 'localhost:57039', '--wait-for-client']` * @param host * @param port * @param waitUntilDebuggerAttaches Defaults to `true`. diff --git a/pythonFiles/.env b/python_files/.env similarity index 100% rename from pythonFiles/.env rename to python_files/.env diff --git a/pythonFiles/.vscode/settings.json b/python_files/.vscode/settings.json similarity index 100% rename from pythonFiles/.vscode/settings.json rename to python_files/.vscode/settings.json diff --git a/pythonFiles/Notebooks intro.ipynb b/python_files/Notebooks intro.ipynb similarity index 100% rename from pythonFiles/Notebooks intro.ipynb rename to python_files/Notebooks intro.ipynb diff --git a/pythonFiles/create_conda.py b/python_files/create_conda.py similarity index 100% rename from pythonFiles/create_conda.py rename to python_files/create_conda.py diff --git a/pythonFiles/create_microvenv.py b/python_files/create_microvenv.py similarity index 100% rename from pythonFiles/create_microvenv.py rename to python_files/create_microvenv.py diff --git a/pythonFiles/create_venv.py b/python_files/create_venv.py similarity index 100% rename from pythonFiles/create_venv.py rename to python_files/create_venv.py diff --git a/pythonFiles/deactivate/bash/deactivate b/python_files/deactivate/bash/deactivate old mode 100755 new mode 100644 similarity index 100% rename from pythonFiles/deactivate/bash/deactivate rename to python_files/deactivate/bash/deactivate diff --git a/pythonFiles/deactivate/fish/deactivate b/python_files/deactivate/fish/deactivate old mode 100755 new mode 100644 similarity index 100% rename from pythonFiles/deactivate/fish/deactivate rename to python_files/deactivate/fish/deactivate diff --git a/pythonFiles/deactivate/powershell/deactivate.ps1 b/python_files/deactivate/powershell/deactivate.ps1 similarity index 100% rename from pythonFiles/deactivate/powershell/deactivate.ps1 rename to python_files/deactivate/powershell/deactivate.ps1 diff --git a/pythonFiles/deactivate/zsh/deactivate b/python_files/deactivate/zsh/deactivate old mode 100755 new mode 100644 similarity index 100% rename from pythonFiles/deactivate/zsh/deactivate rename to python_files/deactivate/zsh/deactivate diff --git a/pythonFiles/download_get_pip.py b/python_files/download_get_pip.py similarity index 96% rename from pythonFiles/download_get_pip.py rename to python_files/download_get_pip.py index b8238d60f261..d87d1cf032fa 100644 --- a/pythonFiles/download_get_pip.py +++ b/python_files/download_get_pip.py @@ -8,7 +8,7 @@ from packaging.version import parse as version_parser EXTENSION_ROOT = pathlib.Path(__file__).parent.parent -GET_PIP_DEST = EXTENSION_ROOT / "pythonFiles" +GET_PIP_DEST = EXTENSION_ROOT / "python_files" PIP_PACKAGE = "pip" PIP_VERSION = "latest" # Can be "latest", or specific version "23.1.2" diff --git a/pythonFiles/get_output_via_markers.py b/python_files/get_output_via_markers.py similarity index 100% rename from pythonFiles/get_output_via_markers.py rename to python_files/get_output_via_markers.py diff --git a/pythonFiles/install_debugpy.py b/python_files/install_debugpy.py similarity index 96% rename from pythonFiles/install_debugpy.py rename to python_files/install_debugpy.py index c217e81fd219..e38ca82230c9 100644 --- a/pythonFiles/install_debugpy.py +++ b/python_files/install_debugpy.py @@ -10,7 +10,7 @@ from packaging.version import parse as version_parser EXTENSION_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -DEBUGGER_DEST = os.path.join(EXTENSION_ROOT, "pythonFiles", "lib", "python") +DEBUGGER_DEST = os.path.join(EXTENSION_ROOT, "python_files", "lib", "python") DEBUGGER_PACKAGE = "debugpy" DEBUGGER_PYTHON_ABI_VERSIONS = ("cp310",) DEBUGGER_VERSION = "1.8.1" # can also be "latest" diff --git a/pythonFiles/installed_check.py b/python_files/installed_check.py similarity index 100% rename from pythonFiles/installed_check.py rename to python_files/installed_check.py diff --git a/pythonFiles/interpreterInfo.py b/python_files/interpreterInfo.py similarity index 100% rename from pythonFiles/interpreterInfo.py rename to python_files/interpreterInfo.py diff --git a/pythonFiles/jedilsp_requirements/requirements.in b/python_files/jedilsp_requirements/requirements.in similarity index 72% rename from pythonFiles/jedilsp_requirements/requirements.in rename to python_files/jedilsp_requirements/requirements.in index 0167b757cf7b..6d5404df8f0f 100644 --- a/pythonFiles/jedilsp_requirements/requirements.in +++ b/python_files/jedilsp_requirements/requirements.in @@ -2,7 +2,7 @@ # To update requirements.txt, run the following commands. # Use Python 3.8 when creating the environment or using pip-tools # 1) pip install pip-tools -# 2) pip-compile --generate-hashes --upgrade pythonFiles\jedilsp_requirements\requirements.in +# 2) pip-compile --generate-hashes --upgrade python_files\jedilsp_requirements\requirements.in jedi-language-server>=0.34.3 pygls>=0.10.3 diff --git a/pythonFiles/jedilsp_requirements/requirements.txt b/python_files/jedilsp_requirements/requirements.txt similarity index 93% rename from pythonFiles/jedilsp_requirements/requirements.txt rename to python_files/jedilsp_requirements/requirements.txt index 889f021670bf..5ee0244deb80 100644 --- a/pythonFiles/jedilsp_requirements/requirements.txt +++ b/python_files/jedilsp_requirements/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.8 # by the following command: # -# pip-compile --generate-hashes 'pythonFiles\jedilsp_requirements\requirements.in' +# pip-compile --generate-hashes 'python_files\jedilsp_requirements\requirements.in' # attrs==23.1.0 \ --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ @@ -35,7 +35,7 @@ jedi==0.19.1 \ jedi-language-server==0.41.1 \ --hash=sha256:3f15ca5cc28e728564f7d63583e171b418025582447ce023512e3f2b2d71ebae \ --hash=sha256:ca9b3e7f48b70f0988d85ffde4f01dd1ab94c8e0f69e8c6424e6657117b44f91 - # via -r pythonFiles\jedilsp_requirements\requirements.in + # via -r python_files\jedilsp_requirements\requirements.in lsprotocol==2023.0.0b1 \ --hash=sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62 \ --hash=sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4 @@ -50,7 +50,7 @@ pygls==1.0.2 \ --hash=sha256:6d278d29fa6559b0f7a448263c85cb64ec6e9369548b02f1a7944060848b21f9 \ --hash=sha256:888ed63d1f650b4fc64d603d73d37545386ec533c0caac921aed80f80ea946a4 # via - # -r pythonFiles\jedilsp_requirements\requirements.in + # -r python_files\jedilsp_requirements\requirements.in # jedi-language-server typeguard==3.0.2 \ --hash=sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e \ diff --git a/pythonFiles/linter.py b/python_files/linter.py similarity index 100% rename from pythonFiles/linter.py rename to python_files/linter.py diff --git a/pythonFiles/normalizeSelection.py b/python_files/normalizeSelection.py similarity index 100% rename from pythonFiles/normalizeSelection.py rename to python_files/normalizeSelection.py diff --git a/pythonFiles/printEnvVariables.py b/python_files/printEnvVariables.py similarity index 100% rename from pythonFiles/printEnvVariables.py rename to python_files/printEnvVariables.py diff --git a/pythonFiles/printEnvVariablesToFile.py b/python_files/printEnvVariablesToFile.py similarity index 100% rename from pythonFiles/printEnvVariablesToFile.py rename to python_files/printEnvVariablesToFile.py diff --git a/pythonFiles/pyproject.toml b/python_files/pyproject.toml similarity index 86% rename from pythonFiles/pyproject.toml rename to python_files/pyproject.toml index d865e27418e0..151e598bd2b2 100644 --- a/pythonFiles/pyproject.toml +++ b/python_files/pyproject.toml @@ -58,10 +58,10 @@ exclude = [ 'tests/testing_tools/adapter/test_util.py', 'tests/testing_tools/adapter/pytest/test_cli.py', 'tests/testing_tools/adapter/pytest/test_discovery.py', - 'pythonFiles/testing_tools/*', - 'pythonFiles/testing_tools/adapter/pytest/__init__.py', - 'pythonFiles/tests/pytestadapter/expected_execution_test_output.py', - 'pythonFiles/tests/unittestadapter/.data/discovery_error/file_one.py', - 'pythonFiles/tests/unittestadapter/test_utils.py', + 'python_files/testing_tools/*', + 'python_files/testing_tools/adapter/pytest/__init__.py', + 'python_files/tests/pytestadapter/expected_execution_test_output.py', + 'python_files/tests/unittestadapter/.data/discovery_error/file_one.py', + 'python_files/tests/unittestadapter/test_utils.py', ] diff --git a/pythonFiles/pythonrc.py b/python_files/pythonrc.py similarity index 100% rename from pythonFiles/pythonrc.py rename to python_files/pythonrc.py diff --git a/pythonFiles/run-jedi-language-server.py b/python_files/run-jedi-language-server.py similarity index 74% rename from pythonFiles/run-jedi-language-server.py rename to python_files/run-jedi-language-server.py index 31095121409f..3239bc7e9c8c 100644 --- a/pythonFiles/run-jedi-language-server.py +++ b/python_files/run-jedi-language-server.py @@ -3,7 +3,7 @@ # Add the lib path to our sys path so jedi_language_server can find its references EXTENSION_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, os.path.join(EXTENSION_ROOT, "pythonFiles", "lib", "jedilsp")) +sys.path.insert(0, os.path.join(EXTENSION_ROOT, "python_files", "lib", "jedilsp")) from jedi_language_server.cli import cli diff --git a/pythonFiles/shell_exec.py b/python_files/shell_exec.py similarity index 100% rename from pythonFiles/shell_exec.py rename to python_files/shell_exec.py diff --git a/pythonFiles/tensorboard_launcher.py b/python_files/tensorboard_launcher.py similarity index 100% rename from pythonFiles/tensorboard_launcher.py rename to python_files/tensorboard_launcher.py diff --git a/pythonFiles/testing_tools/__init__.py b/python_files/testing_tools/__init__.py similarity index 100% rename from pythonFiles/testing_tools/__init__.py rename to python_files/testing_tools/__init__.py diff --git a/pythonFiles/testing_tools/adapter/__init__.py b/python_files/testing_tools/adapter/__init__.py similarity index 100% rename from pythonFiles/testing_tools/adapter/__init__.py rename to python_files/testing_tools/adapter/__init__.py diff --git a/pythonFiles/testing_tools/adapter/__main__.py b/python_files/testing_tools/adapter/__main__.py similarity index 100% rename from pythonFiles/testing_tools/adapter/__main__.py rename to python_files/testing_tools/adapter/__main__.py diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/python_files/testing_tools/adapter/discovery.py similarity index 100% rename from pythonFiles/testing_tools/adapter/discovery.py rename to python_files/testing_tools/adapter/discovery.py diff --git a/pythonFiles/testing_tools/adapter/errors.py b/python_files/testing_tools/adapter/errors.py similarity index 100% rename from pythonFiles/testing_tools/adapter/errors.py rename to python_files/testing_tools/adapter/errors.py diff --git a/pythonFiles/testing_tools/adapter/info.py b/python_files/testing_tools/adapter/info.py similarity index 100% rename from pythonFiles/testing_tools/adapter/info.py rename to python_files/testing_tools/adapter/info.py diff --git a/pythonFiles/testing_tools/adapter/pytest/__init__.py b/python_files/testing_tools/adapter/pytest/__init__.py similarity index 100% rename from pythonFiles/testing_tools/adapter/pytest/__init__.py rename to python_files/testing_tools/adapter/pytest/__init__.py diff --git a/pythonFiles/testing_tools/adapter/pytest/_cli.py b/python_files/testing_tools/adapter/pytest/_cli.py similarity index 100% rename from pythonFiles/testing_tools/adapter/pytest/_cli.py rename to python_files/testing_tools/adapter/pytest/_cli.py diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/python_files/testing_tools/adapter/pytest/_discovery.py similarity index 100% rename from pythonFiles/testing_tools/adapter/pytest/_discovery.py rename to python_files/testing_tools/adapter/pytest/_discovery.py diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/python_files/testing_tools/adapter/pytest/_pytest_item.py similarity index 100% rename from pythonFiles/testing_tools/adapter/pytest/_pytest_item.py rename to python_files/testing_tools/adapter/pytest/_pytest_item.py diff --git a/pythonFiles/testing_tools/adapter/report.py b/python_files/testing_tools/adapter/report.py similarity index 100% rename from pythonFiles/testing_tools/adapter/report.py rename to python_files/testing_tools/adapter/report.py diff --git a/pythonFiles/testing_tools/adapter/util.py b/python_files/testing_tools/adapter/util.py similarity index 100% rename from pythonFiles/testing_tools/adapter/util.py rename to python_files/testing_tools/adapter/util.py diff --git a/pythonFiles/testing_tools/process_json_util.py b/python_files/testing_tools/process_json_util.py similarity index 100% rename from pythonFiles/testing_tools/process_json_util.py rename to python_files/testing_tools/process_json_util.py diff --git a/pythonFiles/testing_tools/run_adapter.py b/python_files/testing_tools/run_adapter.py similarity index 81% rename from pythonFiles/testing_tools/run_adapter.py rename to python_files/testing_tools/run_adapter.py index 1eeef194f8f5..8af4e49dd31c 100644 --- a/pythonFiles/testing_tools/run_adapter.py +++ b/python_files/testing_tools/run_adapter.py @@ -7,8 +7,8 @@ sys.path.insert( 1, - os.path.dirname( # pythonFiles - os.path.dirname( # pythonFiles/testing_tools + os.path.dirname( # python_files + os.path.dirname( # python_files/testing_tools os.path.abspath(__file__) # this file ) ), diff --git a/pythonFiles/testing_tools/socket_manager.py b/python_files/testing_tools/socket_manager.py similarity index 100% rename from pythonFiles/testing_tools/socket_manager.py rename to python_files/testing_tools/socket_manager.py diff --git a/pythonFiles/testing_tools/unittest_discovery.py b/python_files/testing_tools/unittest_discovery.py similarity index 100% rename from pythonFiles/testing_tools/unittest_discovery.py rename to python_files/testing_tools/unittest_discovery.py diff --git a/pythonFiles/testlauncher.py b/python_files/testlauncher.py similarity index 100% rename from pythonFiles/testlauncher.py rename to python_files/testlauncher.py diff --git a/pythonFiles/tests/__init__.py b/python_files/tests/__init__.py similarity index 100% rename from pythonFiles/tests/__init__.py rename to python_files/tests/__init__.py diff --git a/pythonFiles/tests/__main__.py b/python_files/tests/__main__.py similarity index 100% rename from pythonFiles/tests/__main__.py rename to python_files/tests/__main__.py diff --git a/pythonFiles/tests/debug_adapter/__init__.py b/python_files/tests/debug_adapter/__init__.py similarity index 100% rename from pythonFiles/tests/debug_adapter/__init__.py rename to python_files/tests/debug_adapter/__init__.py diff --git a/pythonFiles/tests/debug_adapter/test_install_debugpy.py b/python_files/tests/debug_adapter/test_install_debugpy.py similarity index 100% rename from pythonFiles/tests/debug_adapter/test_install_debugpy.py rename to python_files/tests/debug_adapter/test_install_debugpy.py diff --git a/pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py b/python_files/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py rename to python_files/tests/pytestadapter/.data/dual_level_nested_folder/nested_folder_one/test_bottom_folder.py diff --git a/pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/test_top_folder.py b/python_files/tests/pytestadapter/.data/dual_level_nested_folder/test_top_folder.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/dual_level_nested_folder/test_top_folder.py rename to python_files/tests/pytestadapter/.data/dual_level_nested_folder/test_top_folder.py diff --git a/pythonFiles/tests/pytestadapter/.data/empty_discovery.py b/python_files/tests/pytestadapter/.data/empty_discovery.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/empty_discovery.py rename to python_files/tests/pytestadapter/.data/empty_discovery.py diff --git a/pythonFiles/tests/pytestadapter/.data/error_parametrize_discovery.py b/python_files/tests/pytestadapter/.data/error_parametrize_discovery.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/error_parametrize_discovery.py rename to python_files/tests/pytestadapter/.data/error_parametrize_discovery.py diff --git a/pythonFiles/tests/pytestadapter/.data/error_pytest_import.txt b/python_files/tests/pytestadapter/.data/error_pytest_import.txt similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/error_pytest_import.txt rename to python_files/tests/pytestadapter/.data/error_pytest_import.txt diff --git a/pythonFiles/tests/pytestadapter/.data/error_raise_exception.py b/python_files/tests/pytestadapter/.data/error_raise_exception.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/error_raise_exception.py rename to python_files/tests/pytestadapter/.data/error_raise_exception.py diff --git a/pythonFiles/tests/pytestadapter/.data/error_syntax_discovery.txt b/python_files/tests/pytestadapter/.data/error_syntax_discovery.txt similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/error_syntax_discovery.txt rename to python_files/tests/pytestadapter/.data/error_syntax_discovery.txt diff --git a/pythonFiles/tests/pytestadapter/.data/folder_a/folder_b/folder_a/test_nest.py b/python_files/tests/pytestadapter/.data/folder_a/folder_b/folder_a/test_nest.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/folder_a/folder_b/folder_a/test_nest.py rename to python_files/tests/pytestadapter/.data/folder_a/folder_b/folder_a/test_nest.py diff --git a/pythonFiles/tests/pytestadapter/.data/param_same_name/test_param1.py b/python_files/tests/pytestadapter/.data/param_same_name/test_param1.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/param_same_name/test_param1.py rename to python_files/tests/pytestadapter/.data/param_same_name/test_param1.py diff --git a/pythonFiles/tests/pytestadapter/.data/param_same_name/test_param2.py b/python_files/tests/pytestadapter/.data/param_same_name/test_param2.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/param_same_name/test_param2.py rename to python_files/tests/pytestadapter/.data/param_same_name/test_param2.py diff --git a/pythonFiles/tests/pytestadapter/.data/parametrize_tests.py b/python_files/tests/pytestadapter/.data/parametrize_tests.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/parametrize_tests.py rename to python_files/tests/pytestadapter/.data/parametrize_tests.py diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/pytest.ini b/python_files/tests/pytestadapter/.data/root/tests/pytest.ini similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/root/tests/pytest.ini rename to python_files/tests/pytestadapter/.data/root/tests/pytest.ini diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py b/python_files/tests/pytestadapter/.data/root/tests/test_a.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py rename to python_files/tests/pytestadapter/.data/root/tests/test_a.py diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py b/python_files/tests/pytestadapter/.data/root/tests/test_b.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py rename to python_files/tests/pytestadapter/.data/root/tests/test_b.py diff --git a/pythonFiles/tests/pytestadapter/.data/simple_pytest.py b/python_files/tests/pytestadapter/.data/simple_pytest.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/simple_pytest.py rename to python_files/tests/pytestadapter/.data/simple_pytest.py diff --git a/pythonFiles/tests/pytestadapter/.data/skip_tests.py b/python_files/tests/pytestadapter/.data/skip_tests.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/skip_tests.py rename to python_files/tests/pytestadapter/.data/skip_tests.py diff --git a/pythonFiles/tests/pytestadapter/.data/test_env_vars.py b/python_files/tests/pytestadapter/.data/test_env_vars.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/test_env_vars.py rename to python_files/tests/pytestadapter/.data/test_env_vars.py diff --git a/pythonFiles/tests/pytestadapter/.data/test_logging.py b/python_files/tests/pytestadapter/.data/test_logging.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/test_logging.py rename to python_files/tests/pytestadapter/.data/test_logging.py diff --git a/pythonFiles/tests/pytestadapter/.data/test_multi_class_nest.py b/python_files/tests/pytestadapter/.data/test_multi_class_nest.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/test_multi_class_nest.py rename to python_files/tests/pytestadapter/.data/test_multi_class_nest.py diff --git a/pythonFiles/tests/pytestadapter/.data/text_docstring.txt b/python_files/tests/pytestadapter/.data/text_docstring.txt similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/text_docstring.txt rename to python_files/tests/pytestadapter/.data/text_docstring.txt diff --git a/pythonFiles/tests/pytestadapter/.data/unittest_folder/test_add.py b/python_files/tests/pytestadapter/.data/unittest_folder/test_add.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/unittest_folder/test_add.py rename to python_files/tests/pytestadapter/.data/unittest_folder/test_add.py diff --git a/pythonFiles/tests/pytestadapter/.data/unittest_folder/test_subtract.py b/python_files/tests/pytestadapter/.data/unittest_folder/test_subtract.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/unittest_folder/test_subtract.py rename to python_files/tests/pytestadapter/.data/unittest_folder/test_subtract.py diff --git a/pythonFiles/tests/pytestadapter/.data/unittest_pytest_same_file.py b/python_files/tests/pytestadapter/.data/unittest_pytest_same_file.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/unittest_pytest_same_file.py rename to python_files/tests/pytestadapter/.data/unittest_pytest_same_file.py diff --git a/pythonFiles/tests/pytestadapter/.data/unittest_skiptest_file_level.py b/python_files/tests/pytestadapter/.data/unittest_skiptest_file_level.py similarity index 100% rename from pythonFiles/tests/pytestadapter/.data/unittest_skiptest_file_level.py rename to python_files/tests/pytestadapter/.data/unittest_skiptest_file_level.py diff --git a/pythonFiles/tests/pytestadapter/__init__.py b/python_files/tests/pytestadapter/__init__.py similarity index 100% rename from pythonFiles/tests/pytestadapter/__init__.py rename to python_files/tests/pytestadapter/__init__.py diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/python_files/tests/pytestadapter/expected_discovery_test_output.py similarity index 100% rename from pythonFiles/tests/pytestadapter/expected_discovery_test_output.py rename to python_files/tests/pytestadapter/expected_discovery_test_output.py diff --git a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py b/python_files/tests/pytestadapter/expected_execution_test_output.py similarity index 100% rename from pythonFiles/tests/pytestadapter/expected_execution_test_output.py rename to python_files/tests/pytestadapter/expected_execution_test_output.py diff --git a/pythonFiles/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py similarity index 100% rename from pythonFiles/tests/pytestadapter/helpers.py rename to python_files/tests/pytestadapter/helpers.py diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/python_files/tests/pytestadapter/test_discovery.py similarity index 100% rename from pythonFiles/tests/pytestadapter/test_discovery.py rename to python_files/tests/pytestadapter/test_discovery.py diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/python_files/tests/pytestadapter/test_execution.py similarity index 100% rename from pythonFiles/tests/pytestadapter/test_execution.py rename to python_files/tests/pytestadapter/test_execution.py diff --git a/pythonFiles/tests/run_all.py b/python_files/tests/run_all.py similarity index 100% rename from pythonFiles/tests/run_all.py rename to python_files/tests/run_all.py diff --git a/pythonFiles/tests/test_create_conda.py b/python_files/tests/test_create_conda.py similarity index 100% rename from pythonFiles/tests/test_create_conda.py rename to python_files/tests/test_create_conda.py diff --git a/pythonFiles/tests/test_create_microvenv.py b/python_files/tests/test_create_microvenv.py similarity index 100% rename from pythonFiles/tests/test_create_microvenv.py rename to python_files/tests/test_create_microvenv.py diff --git a/pythonFiles/tests/test_create_venv.py b/python_files/tests/test_create_venv.py similarity index 100% rename from pythonFiles/tests/test_create_venv.py rename to python_files/tests/test_create_venv.py diff --git a/pythonFiles/tests/test_data/missing-deps.data b/python_files/tests/test_data/missing-deps.data similarity index 100% rename from pythonFiles/tests/test_data/missing-deps.data rename to python_files/tests/test_data/missing-deps.data diff --git a/pythonFiles/tests/test_data/no-missing-deps.data b/python_files/tests/test_data/no-missing-deps.data similarity index 100% rename from pythonFiles/tests/test_data/no-missing-deps.data rename to python_files/tests/test_data/no-missing-deps.data diff --git a/pythonFiles/tests/test_data/pyproject-missing-deps.data b/python_files/tests/test_data/pyproject-missing-deps.data similarity index 100% rename from pythonFiles/tests/test_data/pyproject-missing-deps.data rename to python_files/tests/test_data/pyproject-missing-deps.data diff --git a/pythonFiles/tests/test_data/pyproject-no-missing-deps.data b/python_files/tests/test_data/pyproject-no-missing-deps.data similarity index 100% rename from pythonFiles/tests/test_data/pyproject-no-missing-deps.data rename to python_files/tests/test_data/pyproject-no-missing-deps.data diff --git a/pythonFiles/tests/test_dynamic_cursor.py b/python_files/tests/test_dynamic_cursor.py similarity index 100% rename from pythonFiles/tests/test_dynamic_cursor.py rename to python_files/tests/test_dynamic_cursor.py diff --git a/pythonFiles/tests/test_installed_check.py b/python_files/tests/test_installed_check.py similarity index 100% rename from pythonFiles/tests/test_installed_check.py rename to python_files/tests/test_installed_check.py diff --git a/pythonFiles/tests/test_normalize_selection.py b/python_files/tests/test_normalize_selection.py similarity index 98% rename from pythonFiles/tests/test_normalize_selection.py rename to python_files/tests/test_normalize_selection.py index 5f4d6d7d4a1f..60dfddb11e2d 100644 --- a/pythonFiles/tests/test_normalize_selection.py +++ b/python_files/tests/test_normalize_selection.py @@ -5,7 +5,7 @@ import importlib import textwrap -# __file__ = "/Users/anthonykim/Desktop/vscode-python/pythonFiles/normalizeSelection.py" +# __file__ = "/Users/anthonykim/Desktop/vscode-python/python_files/normalizeSelection.py" # sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__)))) import normalizeSelection diff --git a/pythonFiles/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py similarity index 100% rename from pythonFiles/tests/test_shell_integration.py rename to python_files/tests/test_shell_integration.py diff --git a/pythonFiles/tests/test_smart_selection.py b/python_files/tests/test_smart_selection.py similarity index 100% rename from pythonFiles/tests/test_smart_selection.py rename to python_files/tests/test_smart_selection.py diff --git a/pythonFiles/tests/testing_tools/__init__.py b/python_files/tests/testing_tools/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/__init__.py rename to python_files/tests/testing_tools/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/__init__.py b/python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/__init__.py rename to python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/__init__.py b/python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/__init__.py rename to python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/test_Spam.py b/python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/test_Spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/test_Spam.py rename to python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/C/test_Spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/__init__.py b/python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/A/b/__init__.py rename to python_files/tests/testing_tools/adapter/.data/NormCase/tests/A/b/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/__init__.py b/python_files/tests/testing_tools/adapter/.data/NormCase/tests/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/NormCase/tests/__init__.py rename to python_files/tests/testing_tools/adapter/.data/NormCase/tests/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/README.md b/python_files/tests/testing_tools/adapter/.data/complex/README.md similarity index 99% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/README.md rename to python_files/tests/testing_tools/adapter/.data/complex/README.md index e30e96142d02..8840cda1e834 100644 --- a/pythonFiles/tests/testing_tools/adapter/.data/complex/README.md +++ b/python_files/tests/testing_tools/adapter/.data/complex/README.md @@ -1,7 +1,7 @@ ## Directory Structure ``` -pythonFiles/tests/testing_tools/adapter/.data/ +python_files/tests/testing_tools/adapter/.data/ tests/ # test root test_doctest.txt test_pytest.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/mod.py b/python_files/tests/testing_tools/adapter/.data/complex/mod.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/mod.py rename to python_files/tests/testing_tools/adapter/.data/complex/mod.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_42-43.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_42-43.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_42-43.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_42-43.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_42.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_42.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_42.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_42.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_doctest.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_doctest.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_doctest.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_doctest.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_doctest.txt b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_doctest.txt similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_doctest.txt rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_doctest.txt diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_foo.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_foo.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_foo.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_foo.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_mixed.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_mixed.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_mixed.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_mixed.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_pytest.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_pytest.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_pytest.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_pytest.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_pytest_param.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_pytest_param.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_pytest_param.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_pytest_param.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_unittest.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/test_unittest.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/test_unittest.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/test_unittest.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/testspam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/testspam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/testspam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/testspam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/v/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/v/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/v/spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/v/spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_eggs.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_eggs.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_eggs.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_eggs.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_ham.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_ham.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_ham.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_ham.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/v/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/v/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/w/test_spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/w/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/w/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/w/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/w/test_spam_ex.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/w/test_spam_ex.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/w/test_spam_ex.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/w/test_spam_ex.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/test_spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/a/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/__init__.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/__init__.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/test_spam.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/b/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/test_ham.py b/python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/test_ham.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/complex/tests/x/y/z/test_ham.py rename to python_files/tests/testing_tools/adapter/.data/complex/tests/x/y/z/test_ham.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/notests/tests/__init__.py b/python_files/tests/testing_tools/adapter/.data/notests/tests/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/notests/tests/__init__.py rename to python_files/tests/testing_tools/adapter/.data/notests/tests/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/simple/tests/__init__.py b/python_files/tests/testing_tools/adapter/.data/simple/tests/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/simple/tests/__init__.py rename to python_files/tests/testing_tools/adapter/.data/simple/tests/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/simple/tests/test_spam.py b/python_files/tests/testing_tools/adapter/.data/simple/tests/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/simple/tests/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/simple/tests/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/syntax-error/tests/__init__.py b/python_files/tests/testing_tools/adapter/.data/syntax-error/tests/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/syntax-error/tests/__init__.py rename to python_files/tests/testing_tools/adapter/.data/syntax-error/tests/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/.data/syntax-error/tests/test_spam.py b/python_files/tests/testing_tools/adapter/.data/syntax-error/tests/test_spam.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/.data/syntax-error/tests/test_spam.py rename to python_files/tests/testing_tools/adapter/.data/syntax-error/tests/test_spam.py diff --git a/pythonFiles/tests/testing_tools/adapter/__init__.py b/python_files/tests/testing_tools/adapter/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/__init__.py rename to python_files/tests/testing_tools/adapter/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/__init__.py b/python_files/tests/testing_tools/adapter/pytest/__init__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/pytest/__init__.py rename to python_files/tests/testing_tools/adapter/pytest/__init__.py diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_cli.py b/python_files/tests/testing_tools/adapter/pytest/test_cli.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/pytest/test_cli.py rename to python_files/tests/testing_tools/adapter/pytest/test_cli.py diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/python_files/tests/testing_tools/adapter/pytest/test_discovery.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py rename to python_files/tests/testing_tools/adapter/pytest/test_discovery.py diff --git a/pythonFiles/tests/testing_tools/adapter/test___main__.py b/python_files/tests/testing_tools/adapter/test___main__.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/test___main__.py rename to python_files/tests/testing_tools/adapter/test___main__.py diff --git a/pythonFiles/tests/testing_tools/adapter/test_discovery.py b/python_files/tests/testing_tools/adapter/test_discovery.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/test_discovery.py rename to python_files/tests/testing_tools/adapter/test_discovery.py diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/python_files/tests/testing_tools/adapter/test_functional.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/test_functional.py rename to python_files/tests/testing_tools/adapter/test_functional.py diff --git a/pythonFiles/tests/testing_tools/adapter/test_report.py b/python_files/tests/testing_tools/adapter/test_report.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/test_report.py rename to python_files/tests/testing_tools/adapter/test_report.py diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/python_files/tests/testing_tools/adapter/test_util.py similarity index 100% rename from pythonFiles/tests/testing_tools/adapter/test_util.py rename to python_files/tests/testing_tools/adapter/test_util.py diff --git a/pythonFiles/tests/tree_comparison_helper.py b/python_files/tests/tree_comparison_helper.py similarity index 100% rename from pythonFiles/tests/tree_comparison_helper.py rename to python_files/tests/tree_comparison_helper.py diff --git a/pythonFiles/tests/unittestadapter/.data/discovery_empty.py b/python_files/tests/unittestadapter/.data/discovery_empty.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/discovery_empty.py rename to python_files/tests/unittestadapter/.data/discovery_empty.py diff --git a/pythonFiles/tests/unittestadapter/.data/discovery_error/file_one.py b/python_files/tests/unittestadapter/.data/discovery_error/file_one.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/discovery_error/file_one.py rename to python_files/tests/unittestadapter/.data/discovery_error/file_one.py diff --git a/pythonFiles/tests/unittestadapter/.data/discovery_error/file_two.py b/python_files/tests/unittestadapter/.data/discovery_error/file_two.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/discovery_error/file_two.py rename to python_files/tests/unittestadapter/.data/discovery_error/file_two.py diff --git a/pythonFiles/tests/unittestadapter/.data/discovery_simple.py b/python_files/tests/unittestadapter/.data/discovery_simple.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/discovery_simple.py rename to python_files/tests/unittestadapter/.data/discovery_simple.py diff --git a/pythonFiles/tests/unittestadapter/.data/test_fail_simple.py b/python_files/tests/unittestadapter/.data/test_fail_simple.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/test_fail_simple.py rename to python_files/tests/unittestadapter/.data/test_fail_simple.py diff --git a/pythonFiles/tests/unittestadapter/.data/test_subtest.py b/python_files/tests/unittestadapter/.data/test_subtest.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/test_subtest.py rename to python_files/tests/unittestadapter/.data/test_subtest.py diff --git a/pythonFiles/tests/unittestadapter/.data/test_two_classes.py b/python_files/tests/unittestadapter/.data/test_two_classes.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/test_two_classes.py rename to python_files/tests/unittestadapter/.data/test_two_classes.py diff --git a/pythonFiles/tests/unittestadapter/.data/two_patterns/pattern_a_test.py b/python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/two_patterns/pattern_a_test.py rename to python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py diff --git a/pythonFiles/tests/unittestadapter/.data/two_patterns/test_pattern_b.py b/python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/two_patterns/test_pattern_b.py rename to python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py diff --git a/pythonFiles/tests/unittestadapter/.data/unittest_folder/test_add.py b/python_files/tests/unittestadapter/.data/unittest_folder/test_add.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/unittest_folder/test_add.py rename to python_files/tests/unittestadapter/.data/unittest_folder/test_add.py diff --git a/pythonFiles/tests/unittestadapter/.data/unittest_folder/test_subtract.py b/python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/unittest_folder/test_subtract.py rename to python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py diff --git a/pythonFiles/tests/unittestadapter/.data/unittest_skip/unittest_skip_file.py b/python_files/tests/unittestadapter/.data/unittest_skip/unittest_skip_file.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/unittest_skip/unittest_skip_file.py rename to python_files/tests/unittestadapter/.data/unittest_skip/unittest_skip_file.py diff --git a/pythonFiles/tests/unittestadapter/.data/unittest_skip/unittest_skip_function.py b/python_files/tests/unittestadapter/.data/unittest_skip/unittest_skip_function.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/unittest_skip/unittest_skip_function.py rename to python_files/tests/unittestadapter/.data/unittest_skip/unittest_skip_function.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/__init__.py b/python_files/tests/unittestadapter/.data/utils_complex_tree/__init__.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_complex_tree/__init__.py rename to python_files/tests/unittestadapter/.data/utils_complex_tree/__init__.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py b/python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py rename to python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/__init__.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py b/python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py rename to python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/__init__.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py b/python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py rename to python_files/tests/unittestadapter/.data/utils_complex_tree/test_outer_folder/test_inner_folder/test_utils_complex_tree.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_decorated_tree.py b/python_files/tests/unittestadapter/.data/utils_decorated_tree.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_decorated_tree.py rename to python_files/tests/unittestadapter/.data/utils_decorated_tree.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_nested_cases/file_one.py b/python_files/tests/unittestadapter/.data/utils_nested_cases/file_one.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_nested_cases/file_one.py rename to python_files/tests/unittestadapter/.data/utils_nested_cases/file_one.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_nested_cases/folder/__init__.py b/python_files/tests/unittestadapter/.data/utils_nested_cases/folder/__init__.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_nested_cases/folder/__init__.py rename to python_files/tests/unittestadapter/.data/utils_nested_cases/folder/__init__.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_nested_cases/folder/file_two.py b/python_files/tests/unittestadapter/.data/utils_nested_cases/folder/file_two.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_nested_cases/folder/file_two.py rename to python_files/tests/unittestadapter/.data/utils_nested_cases/folder/file_two.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_simple_cases.py b/python_files/tests/unittestadapter/.data/utils_simple_cases.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_simple_cases.py rename to python_files/tests/unittestadapter/.data/utils_simple_cases.py diff --git a/pythonFiles/tests/unittestadapter/.data/utils_simple_tree.py b/python_files/tests/unittestadapter/.data/utils_simple_tree.py similarity index 100% rename from pythonFiles/tests/unittestadapter/.data/utils_simple_tree.py rename to python_files/tests/unittestadapter/.data/utils_simple_tree.py diff --git a/pythonFiles/tests/unittestadapter/__init__.py b/python_files/tests/unittestadapter/__init__.py similarity index 100% rename from pythonFiles/tests/unittestadapter/__init__.py rename to python_files/tests/unittestadapter/__init__.py diff --git a/pythonFiles/tests/unittestadapter/conftest.py b/python_files/tests/unittestadapter/conftest.py similarity index 100% rename from pythonFiles/tests/unittestadapter/conftest.py rename to python_files/tests/unittestadapter/conftest.py diff --git a/pythonFiles/tests/unittestadapter/expected_discovery_test_output.py b/python_files/tests/unittestadapter/expected_discovery_test_output.py similarity index 100% rename from pythonFiles/tests/unittestadapter/expected_discovery_test_output.py rename to python_files/tests/unittestadapter/expected_discovery_test_output.py diff --git a/pythonFiles/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py similarity index 100% rename from pythonFiles/tests/unittestadapter/test_discovery.py rename to python_files/tests/unittestadapter/test_discovery.py diff --git a/pythonFiles/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py similarity index 100% rename from pythonFiles/tests/unittestadapter/test_execution.py rename to python_files/tests/unittestadapter/test_execution.py diff --git a/pythonFiles/tests/unittestadapter/test_utils.py b/python_files/tests/unittestadapter/test_utils.py similarity index 100% rename from pythonFiles/tests/unittestadapter/test_utils.py rename to python_files/tests/unittestadapter/test_utils.py diff --git a/pythonFiles/tests/util.py b/python_files/tests/util.py similarity index 100% rename from pythonFiles/tests/util.py rename to python_files/tests/util.py diff --git a/pythonFiles/unittestadapter/__init__.py b/python_files/unittestadapter/__init__.py similarity index 100% rename from pythonFiles/unittestadapter/__init__.py rename to python_files/unittestadapter/__init__.py diff --git a/pythonFiles/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py similarity index 100% rename from pythonFiles/unittestadapter/discovery.py rename to python_files/unittestadapter/discovery.py diff --git a/pythonFiles/unittestadapter/execution.py b/python_files/unittestadapter/execution.py similarity index 100% rename from pythonFiles/unittestadapter/execution.py rename to python_files/unittestadapter/execution.py diff --git a/pythonFiles/unittestadapter/pvsc_utils.py b/python_files/unittestadapter/pvsc_utils.py similarity index 100% rename from pythonFiles/unittestadapter/pvsc_utils.py rename to python_files/unittestadapter/pvsc_utils.py diff --git a/pythonFiles/visualstudio_py_testlauncher.py b/python_files/visualstudio_py_testlauncher.py similarity index 100% rename from pythonFiles/visualstudio_py_testlauncher.py rename to python_files/visualstudio_py_testlauncher.py diff --git a/pythonFiles/vscode_datascience_helpers/tests/logParser.py b/python_files/vscode_datascience_helpers/tests/logParser.py similarity index 100% rename from pythonFiles/vscode_datascience_helpers/tests/logParser.py rename to python_files/vscode_datascience_helpers/tests/logParser.py diff --git a/pythonFiles/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py similarity index 100% rename from pythonFiles/vscode_pytest/__init__.py rename to python_files/vscode_pytest/__init__.py diff --git a/pythonFiles/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py similarity index 100% rename from pythonFiles/vscode_pytest/run_pytest_script.py rename to python_files/vscode_pytest/run_pytest_script.py diff --git a/src/client/activation/jedi/languageClientFactory.ts b/src/client/activation/jedi/languageClientFactory.ts index c3ef8d9623f3..70bd65da8d0d 100644 --- a/src/client/activation/jedi/languageClientFactory.ts +++ b/src/client/activation/jedi/languageClientFactory.ts @@ -21,7 +21,7 @@ export class JediLanguageClientFactory implements ILanguageClientFactory { clientOptions: LanguageClientOptions, ): Promise { // Just run the language server using a module - const lsScriptPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'run-jedi-language-server.py'); + const lsScriptPath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'run-jedi-language-server.py'); const interpreter = await this.interpreterService.getActiveInterpreter(resource); const serverOptions: ServerOptions = { command: interpreter ? interpreter.path : 'python', diff --git a/src/client/activation/jedi/manager.ts b/src/client/activation/jedi/manager.ts index 672e9a1b33fd..bafdcc735a12 100644 --- a/src/client/activation/jedi/manager.ts +++ b/src/client/activation/jedi/manager.ts @@ -68,7 +68,7 @@ export class JediLanguageServerManager implements ILanguageServerManager { try { // Version is actually hardcoded in our requirements.txt. const requirementsTxt = await fs.readFile( - path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'jedilsp_requirements', 'requirements.txt'), + path.join(EXTENSION_ROOT_DIR, 'python_files', 'jedilsp_requirements', 'requirements.txt'), 'utf-8', ); diff --git a/src/client/api/types.ts b/src/client/api/types.ts index 4de554bf5a24..dccbd78f6f04 100644 --- a/src/client/api/types.ts +++ b/src/client/api/types.ts @@ -16,7 +16,7 @@ export interface PythonExtension { /** * Generate an array of strings for commands to pass to the Python executable to launch the debugger for remote debugging. * Users can append another array of strings of what they want to execute along with relevant arguments to Python. - * E.g `['/Users/..../pythonVSCode/pythonFiles/lib/python/debugpy', '--listen', 'localhost:57039', '--wait-for-client']` + * E.g `['/Users/..../pythonVSCode/python_files/lib/python/debugpy', '--listen', 'localhost:57039', '--wait-for-client']` * @param host * @param port * @param waitUntilDebuggerAttaches Defaults to `true`. diff --git a/src/client/common/process/internal/scripts/constants.ts b/src/client/common/process/internal/scripts/constants.ts index 4448f7e639ce..6954592ed3dd 100644 --- a/src/client/common/process/internal/scripts/constants.ts +++ b/src/client/common/process/internal/scripts/constants.ts @@ -5,4 +5,4 @@ import * as path from 'path'; import { EXTENSION_ROOT_DIR } from '../../../constants'; // It is simpler to hard-code it instead of using vscode.ExtensionContext.extensionPath. -export const _SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); +export const _SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, 'python_files'); diff --git a/src/client/common/process/internal/scripts/index.ts b/src/client/common/process/internal/scripts/index.ts index 2644fd0d00ff..f2c905c02889 100644 --- a/src/client/common/process/internal/scripts/index.ts +++ b/src/client/common/process/internal/scripts/index.ts @@ -7,7 +7,7 @@ import { _SCRIPTS_DIR } from './constants'; const SCRIPTS_DIR = _SCRIPTS_DIR; // "scripts" contains everything relevant to the scripts found under -// the top-level "pythonFiles" directory. Each of those scripts has +// the top-level "python_files" directory. Each of those scripts has // a function in this module which matches the script's filename. // Each function provides the commandline arguments that should be // used when invoking a Python executable, whether through spawn/exec @@ -18,7 +18,7 @@ const SCRIPTS_DIR = _SCRIPTS_DIR; // into the corresponding object or objects. "parse()" takes a single // string as the stdout text and returns the relevant data. // -// Some of the scripts are located in subdirectories of "pythonFiles". +// Some of the scripts are located in subdirectories of "python_files". // For each of those subdirectories there is a sub-module where // those scripts' functions may be found. // diff --git a/src/client/common/terminal/service.ts b/src/client/common/terminal/service.ts index cf63603a2e9b..c3b90181d563 100644 --- a/src/client/common/terminal/service.ts +++ b/src/client/common/terminal/service.ts @@ -31,7 +31,7 @@ export class TerminalService implements ITerminalService, Disposable { private terminalHelper: ITerminalHelper; private terminalActivator: ITerminalActivator; private terminalAutoActivator: ITerminalAutoActivation; - private readonly envVarScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pythonrc.py'); + private readonly envVarScript = path.join(EXTENSION_ROOT_DIR, 'python_files', 'pythonrc.py'); public get onDidCloseTerminal(): Event { return this.terminalClosed.event.bind(this.terminalClosed); } diff --git a/src/client/debugger/extension/adapter/factory.ts b/src/client/debugger/extension/adapter/factory.ts index ecbd8afcc287..cfc8cf91aba3 100644 --- a/src/client/debugger/extension/adapter/factory.ts +++ b/src/client/debugger/extension/adapter/factory.ts @@ -93,7 +93,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac const debuggerAdapterPathToUse = path.join( EXTENSION_ROOT_DIR, - 'pythonFiles', + 'python_files', 'lib', 'python', 'debugpy', diff --git a/src/client/debugger/extension/adapter/remoteLaunchers.ts b/src/client/debugger/extension/adapter/remoteLaunchers.ts index f42f101f8523..80e0289e3ad8 100644 --- a/src/client/debugger/extension/adapter/remoteLaunchers.ts +++ b/src/client/debugger/extension/adapter/remoteLaunchers.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { EXTENSION_ROOT_DIR } from '../../../common/constants'; import '../../../common/extensions'; -const pathToPythonLibDir = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python'); +const pathToPythonLibDir = path.join(EXTENSION_ROOT_DIR, 'python_files', 'lib', 'python'); const pathToDebugger = path.join(pathToPythonLibDir, 'debugpy'); type RemoteDebugOptions = { diff --git a/src/client/jupyter/jupyterIntegration.ts b/src/client/jupyter/jupyterIntegration.ts index 9faab6d2a763..a385248bff06 100644 --- a/src/client/jupyter/jupyterIntegration.ts +++ b/src/client/jupyter/jupyterIntegration.ts @@ -34,7 +34,7 @@ type PythonApiForJupyterExtension = { */ getSuggestions(resource: Resource): Promise; /** - * Returns path to where `debugpy` is. In python extension this is `/pythonFiles/lib/python`. + * Returns path to where `debugpy` is. In python extension this is `/python_files/lib/python`. */ getDebuggerPath(): Promise; /** diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index 85076461f22e..f05fa6bc9373 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -87,7 +87,7 @@ export class DebugLauncher implements ITestDebugLauncher { debugConfig.rules = []; } debugConfig.rules.push({ - path: path.join(EXTENSION_ROOT_DIR, 'pythonFiles'), + path: path.join(EXTENSION_ROOT_DIR, 'python_files'), include: false, }); @@ -219,7 +219,7 @@ export class DebugLauncher implements ITestDebugLauncher { ); } } - const pluginPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); + const pluginPath = path.join(EXTENSION_ROOT_DIR, 'python_files'); // check if PYTHONPATH is already set in the environment variables if (launchArgs.env) { const additionalPythonPath = [pluginPath]; diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index e51270eb4f9e..685f36af007a 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -228,7 +228,7 @@ export interface ITestExecutionAdapter { ): Promise; } -// Same types as in pythonFiles/unittestadapter/utils.py +// Same types as in python_files/unittestadapter/utils.py export type DiscoveredTestType = 'folder' | 'file' | 'class' | 'test'; export type DiscoveredTestCommon = { diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index ab44c96821e5..2d0dab765088 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -67,7 +67,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } async runPytestDiscovery(uri: Uri, uuid: string, executionFactory?: IPythonExecutionFactory): Promise { - const relativePathToPytest = 'pythonFiles'; + const relativePathToPytest = 'python_files'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); const settings = this.configSettings.getSettings(uri); let pytestArgsMap = argsToMap(settings.testing.pytestArgs); diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index d366bdfc9718..de519548d686 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -103,7 +103,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { debugLauncher?: ITestDebugLauncher, deferredTillEOT?: Deferred, ): Promise { - const relativePathToPytest = 'pythonFiles'; + const relativePathToPytest = 'python_files'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 75e29afc9712..8cc44b3783c5 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -83,7 +83,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { - const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'discovery.py'); return { script: discoveryScript, diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index d90581a93110..85fd01f093a6 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -109,7 +109,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { } function buildExecutionCommand(args: string[]): TestExecutionCommand { - const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); + const executionScript = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'execution.py'); return { script: executionScript, diff --git a/src/test/api.functional.test.ts b/src/test/api.functional.test.ts index d99bcfde81ab..851d56c00e07 100644 --- a/src/test/api.functional.test.ts +++ b/src/test/api.functional.test.ts @@ -19,7 +19,7 @@ import { IServiceContainer, IServiceManager } from '../client/ioc/types'; import { IDiscoveryAPI } from '../client/pythonEnvironments/base/locator'; suite('Extension API', () => { - const debuggerPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'debugpy'); + const debuggerPath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'lib', 'python', 'debugpy'); const debuggerHost = 'somehost'; const debuggerPort = 12345; diff --git a/src/test/common.ts b/src/test/common.ts index 2ef366a3a472..bbf48f0e14c7 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -22,7 +22,7 @@ const StreamZip = require('node-stream-zip'); export { sleep } from './core'; -const fileInNonRootWorkspace = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'pythonFiles', 'dummy.py'); +const fileInNonRootWorkspace = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'python_files', 'dummy.py'); export const rootWorkspaceUri = getWorkspaceRoot(); export const PYTHON_PATH = getPythonPath(); diff --git a/src/test/common/terminals/service.unit.test.ts b/src/test/common/terminals/service.unit.test.ts index 7336f7094e6e..61556e3df2d1 100644 --- a/src/test/common/terminals/service.unit.test.ts +++ b/src/test/common/terminals/service.unit.test.ts @@ -170,7 +170,7 @@ suite('Terminal Service', () => { .setup((t) => t.createTerminal(TypeMoq.It.isAny())) .returns(() => terminal.object) .verifiable(TypeMoq.Times.atLeastOnce()); - const envVarScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pythonrc.py'); + const envVarScript = path.join(EXTENSION_ROOT_DIR, 'python_files', 'pythonrc.py'); terminalManager .setup((t) => t.createTerminal({ diff --git a/src/test/common/terminals/synchronousTerminalService.unit.test.ts b/src/test/common/terminals/synchronousTerminalService.unit.test.ts index f74c529ef470..4b6e77ec8095 100644 --- a/src/test/common/terminals/synchronousTerminalService.unit.test.ts +++ b/src/test/common/terminals/synchronousTerminalService.unit.test.ts @@ -66,7 +66,7 @@ suite('Terminal Service (synchronous)', () => { }); }); suite('sendCommand', () => { - const shellExecFile = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'shell_exec.py'); + const shellExecFile = path.join(EXTENSION_ROOT_DIR, 'python_files', 'shell_exec.py'); test('run sendCommand in terminalService if there is no cancellation token', async () => { when(terminalService.sendCommand('cmd', deepEqual(['1', '2']))).thenResolve(); diff --git a/src/test/debugger/extension/adapter/adapter.test.ts b/src/test/debugger/extension/adapter/adapter.test.ts index 7e20d5b930b9..2f60290897af 100644 --- a/src/test/debugger/extension/adapter/adapter.test.ts +++ b/src/test/debugger/extension/adapter/adapter.test.ts @@ -19,7 +19,7 @@ function resolveWSFile(wsRoot: string, ...filePath: string[]): string { } suite('Debugger Integration', () => { - const file = resolveWSFile(WS_ROOT, 'pythonFiles', 'debugging', 'wait_for_file.py'); + const file = resolveWSFile(WS_ROOT, 'python_files', 'debugging', 'wait_for_file.py'); const doneFile = resolveWSFile(WS_ROOT, 'should-not-exist'); const outFile = resolveWSFile(WS_ROOT, 'output.txt'); const resource = vscode.Uri.file(file); diff --git a/src/test/debugger/extension/adapter/factory.unit.test.ts b/src/test/debugger/extension/adapter/factory.unit.test.ts index 5728bf0c34cd..172c2715b086 100644 --- a/src/test/debugger/extension/adapter/factory.unit.test.ts +++ b/src/test/debugger/extension/adapter/factory.unit.test.ts @@ -41,7 +41,7 @@ suite('Debugging - Adapter Factory', () => { let commandManager: ICommandManager; const nodeExecutable = undefined; - const debugAdapterPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'debugpy', 'adapter'); + const debugAdapterPath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'lib', 'python', 'debugpy', 'adapter'); const pythonPath = path.join('path', 'to', 'python', 'interpreter'); const interpreter = { architecture: Architecture.Unknown, diff --git a/src/test/debugger/extension/adapter/remoteLaunchers.unit.test.ts b/src/test/debugger/extension/adapter/remoteLaunchers.unit.test.ts index aa520f66faa6..2a75f6316a09 100644 --- a/src/test/debugger/extension/adapter/remoteLaunchers.unit.test.ts +++ b/src/test/debugger/extension/adapter/remoteLaunchers.unit.test.ts @@ -52,7 +52,7 @@ suite('External debugpy Debugger Launcher', () => { }); suite('Path To Debugger Package', () => { - const pathToPythonLibDir = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python'); + const pathToPythonLibDir = path.join(EXTENSION_ROOT_DIR, 'python_files', 'lib', 'python'); test('Path to debugpy debugger package', () => { const actual = launchers.getDebugpyPackagePath(); const expected = path.join(pathToPythonLibDir, 'debugpy'); diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 487860410bf0..9c3f0ac387a7 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -14,7 +14,7 @@ import { sleep } from './core'; export * from './constants'; export * from './ciConstants'; -const dummyPythonFile = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py'); +const dummyPythonFile = path.join(__dirname, '..', '..', 'src', 'test', 'python_files', 'dummy.py'); export const multirootPath = path.join(__dirname, '..', '..', 'src', 'testMultiRootWkspc'); const workspace3Uri = vscode.Uri.file(path.join(multirootPath, 'workspace3')); diff --git a/src/test/interpreters/activation/service.unit.test.ts b/src/test/interpreters/activation/service.unit.test.ts index 9b2c121c89b4..a0f9b3bd6915 100644 --- a/src/test/interpreters/activation/service.unit.test.ts +++ b/src/test/interpreters/activation/service.unit.test.ts @@ -161,7 +161,11 @@ suite('Interpreters Activation - Python Environment Variables', () => { const shellCmd = capture(processService.shellExec).first()[0]; - const printEnvPyFile = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'printEnvVariables.py'); + const printEnvPyFile = path.join( + EXTENSION_ROOT_DIR, + 'python_files', + 'printEnvVariables.py', + ); const expectedCommand = [ ...cmd, `echo '${getEnvironmentPrefix}'`, diff --git a/src/test/pythonEnvironments/info/interpreter.unit.test.ts b/src/test/pythonEnvironments/info/interpreter.unit.test.ts index 38a916d1db9b..967454dd6c7e 100644 --- a/src/test/pythonEnvironments/info/interpreter.unit.test.ts +++ b/src/test/pythonEnvironments/info/interpreter.unit.test.ts @@ -11,7 +11,7 @@ import { buildPythonExecInfo } from '../../../client/pythonEnvironments/exec'; import { getInterpreterInfo } from '../../../client/pythonEnvironments/info/interpreter'; import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../../constants'; -const script = pathJoin(EXTENSION_ROOT_DIR_FOR_TESTS, 'pythonFiles', 'interpreterInfo.py'); +const script = pathJoin(EXTENSION_ROOT_DIR_FOR_TESTS, 'python_files', 'interpreterInfo.py'); suite('extractInterpreterInfo()', () => { // Tests go here. diff --git a/src/test/pythonFiles/formatting/autoPep8Formatted.py b/src/test/pythonFiles/formatting/autoPep8Formatted.py deleted file mode 100644 index e63158d6d4fd..000000000000 --- a/src/test/pythonFiles/formatting/autoPep8Formatted.py +++ /dev/null @@ -1,32 +0,0 @@ - -import math -import sys - - -def example1(): - # This is a long comment. This should be wrapped to fit within 72 characters. - some_tuple = (1, 2, 3, 'a') - some_variable = {'long': 'Long code lines should be wrapped within 79 characters.', - 'other': [math.pi, 100, 200, 300, 9876543210, 'This is a long string that goes on'], - 'more': {'inner': 'This whole logical line should be wrapped.', some_tuple: [1, - 20, 300, 40000, 500000000, 60000000000000000]}} - return (some_tuple, some_variable) - - -def example2(): return {'has_key() is deprecated': True}.has_key( - {'f': 2}.has_key('')) - - -class Example3(object): - def __init__(self, bar): - # Comments should have a space after the hash. - if bar: - bar += 1 - bar = bar * bar - return bar - else: - some_string = """ - Indentation in multiline strings should not be touched. -Only actual code should be reindented. -""" - return (sys.path, some_string) diff --git a/src/test/pythonFiles/formatting/autopep8.output b/src/test/pythonFiles/formatting/autopep8.output deleted file mode 100644 index 80cb3a445811..000000000000 --- a/src/test/pythonFiles/formatting/autopep8.output +++ /dev/null @@ -1,50 +0,0 @@ ---- original/C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py -+++ fixed/C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py -@@ -1,22 +1,32 @@ - --import math, sys; -+import math -+import sys -+ - - def example1(): -- ####This is a long comment. This should be wrapped to fit within 72 characters. -- some_tuple=( 1,2, 3,'a' ); -- some_variable={'long':'Long code lines should be wrapped within 79 characters.', -- 'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'], -- 'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1, -- 20,300,40000,500000000,60000000000000000]}} -+ # This is a long comment. This should be wrapped to fit within 72 characters. -+ some_tuple = (1, 2, 3, 'a') -+ some_variable = {'long': 'Long code lines should be wrapped within 79 characters.', -+ 'other': [math.pi, 100, 200, 300, 9876543210, 'This is a long string that goes on'], -+ 'more': {'inner': 'This whole logical line should be wrapped.', some_tuple: [1, -+ 20, 300, 40000, 500000000, 60000000000000000]}} - return (some_tuple, some_variable) --def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key('')); --class Example3( object ): -- def __init__ ( self, bar ): -- #Comments should have a space after the hash. -- if bar : bar+=1; bar=bar* bar ; return bar -- else: -- some_string = """ -+ -+ -+def example2(): return {'has_key() is deprecated': True}.has_key( -+ {'f': 2}.has_key('')) -+ -+ -+class Example3(object): -+ def __init__(self, bar): -+ # Comments should have a space after the hash. -+ if bar: -+ bar += 1 -+ bar = bar * bar -+ return bar -+ else: -+ some_string = """ - Indentation in multiline strings should not be touched. - Only actual code should be reindented. - """ -- return (sys.path, some_string) -+ return (sys.path, some_string) \ No newline at end of file diff --git a/src/test/pythonFiles/formatting/black.output b/src/test/pythonFiles/formatting/black.output deleted file mode 100644 index 4c14d61f2b9b..000000000000 --- a/src/test/pythonFiles/formatting/black.output +++ /dev/null @@ -1,59 +0,0 @@ ---- C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py 2020-05-11 18:56:39.835398 +0000 -+++ C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py 2020-05-11 19:05:50.969508 +0000 -@@ -1,23 +1,42 @@ -+import math, sys - --import math, sys; - - def example1(): - ####This is a long comment. This should be wrapped to fit within 72 characters. -- some_tuple=( 1,2, 3,'a' ); -- some_variable={'long':'Long code lines should be wrapped within 79 characters.', -- 'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'], -- 'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1, -- 20,300,40000,500000000,60000000000000000]}} -+ some_tuple = (1, 2, 3, "a") -+ some_variable = { -+ "long": "Long code lines should be wrapped within 79 characters.", -+ "other": [ -+ math.pi, -+ 100, -+ 200, -+ 300, -+ 9876543210, -+ "This is a long string that goes on", -+ ], -+ "more": { -+ "inner": "This whole logical line should be wrapped.", -+ some_tuple: [1, 20, 300, 40000, 500000000, 60000000000000000], -+ }, -+ } - return (some_tuple, some_variable) --def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key('')); --class Example3( object ): -- def __init__ ( self, bar ): -- #Comments should have a space after the hash. -- if bar : bar+=1; bar=bar* bar ; return bar -- else: -- some_string = """ -+ -+ -+def example2(): -+ return {"has_key() is deprecated": True}.has_key({"f": 2}.has_key("")) -+ -+ -+class Example3(object): -+ def __init__(self, bar): -+ # Comments should have a space after the hash. -+ if bar: -+ bar += 1 -+ bar = bar * bar -+ return bar -+ else: -+ some_string = """ - Indentation in multiline strings should not be touched. - Only actual code should be reindented. - """ -- return (sys.path, some_string) -+ return (sys.path, some_string) - \ No newline at end of file diff --git a/src/test/pythonFiles/formatting/blackFormatted.py b/src/test/pythonFiles/formatting/blackFormatted.py deleted file mode 100644 index e7bca8b1298c..000000000000 --- a/src/test/pythonFiles/formatting/blackFormatted.py +++ /dev/null @@ -1,41 +0,0 @@ -import math, sys - - -def example1(): - ####This is a long comment. This should be wrapped to fit within 72 characters. - some_tuple = (1, 2, 3, "a") - some_variable = { - "long": "Long code lines should be wrapped within 79 characters.", - "other": [ - math.pi, - 100, - 200, - 300, - 9876543210, - "This is a long string that goes on", - ], - "more": { - "inner": "This whole logical line should be wrapped.", - some_tuple: [1, 20, 300, 40000, 500000000, 60000000000000000], - }, - } - return (some_tuple, some_variable) - - -def example2(): - return {"has_key() is deprecated": True}.has_key({"f": 2}.has_key("")) - - -class Example3(object): - def __init__(self, bar): - # Comments should have a space after the hash. - if bar: - bar += 1 - bar = bar * bar - return bar - else: - some_string = """ - Indentation in multiline strings should not be touched. -Only actual code should be reindented. -""" - return (sys.path, some_string) diff --git a/src/test/pythonFiles/formatting/dummy.ts b/src/test/pythonFiles/formatting/dummy.ts deleted file mode 100644 index cbab6669e3b8..000000000000 --- a/src/test/pythonFiles/formatting/dummy.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Dummy ts file to ensure this folder gets created in output directory. - -// Code to ensure linter doesn't complain about empty files. -const a = '1'; diff --git a/src/test/pythonFiles/formatting/fileToFormat.py b/src/test/pythonFiles/formatting/fileToFormat.py deleted file mode 100644 index 5b544bd8504d..000000000000 --- a/src/test/pythonFiles/formatting/fileToFormat.py +++ /dev/null @@ -1,22 +0,0 @@ - -import math, sys; - -def example1(): - ####This is a long comment. This should be wrapped to fit within 72 characters. - some_tuple=( 1,2, 3,'a' ); - some_variable={'long':'Long code lines should be wrapped within 79 characters.', - 'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'], - 'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1, - 20,300,40000,500000000,60000000000000000]}} - return (some_tuple, some_variable) -def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key('')); -class Example3( object ): - def __init__ ( self, bar ): - #Comments should have a space after the hash. - if bar : bar+=1; bar=bar* bar ; return bar - else: - some_string = """ - Indentation in multiline strings should not be touched. -Only actual code should be reindented. -""" - return (sys.path, some_string) diff --git a/src/test/pythonFiles/formatting/fileToFormatOnEnter.py b/src/test/pythonFiles/formatting/fileToFormatOnEnter.py deleted file mode 100644 index 8adfd1fa1233..000000000000 --- a/src/test/pythonFiles/formatting/fileToFormatOnEnter.py +++ /dev/null @@ -1,13 +0,0 @@ -x=1 -"""x=1 -""" - # comment -# x=1 -x+1 # -@x -x.y -if x<=1: -if 1<=x: -def __init__(self, age = 23) -while(1) -x+""" diff --git a/src/test/pythonFiles/formatting/formatWhenDirty.py b/src/test/pythonFiles/formatting/formatWhenDirty.py deleted file mode 100644 index 3fe1b80fde86..000000000000 --- a/src/test/pythonFiles/formatting/formatWhenDirty.py +++ /dev/null @@ -1,3 +0,0 @@ -x = 0 -if x > 0: - x = 1 diff --git a/src/test/pythonFiles/formatting/formatWhenDirtyResult.py b/src/test/pythonFiles/formatting/formatWhenDirtyResult.py deleted file mode 100644 index d0ae06a2a59b..000000000000 --- a/src/test/pythonFiles/formatting/formatWhenDirtyResult.py +++ /dev/null @@ -1,3 +0,0 @@ -x = 0 -if x > 0: - x = 1 diff --git a/src/test/pythonFiles/formatting/pythonGrammar.py b/src/test/pythonFiles/formatting/pythonGrammar.py deleted file mode 100644 index 937cba401d3f..000000000000 --- a/src/test/pythonFiles/formatting/pythonGrammar.py +++ /dev/null @@ -1,1572 +0,0 @@ -# Python test set -- part 1, grammar. -# This just tests whether the parser accepts them all. - -from test.support import check_syntax_error -import inspect -import unittest -import sys -# testing import * -from sys import * - -# different import patterns to check that __annotations__ does not interfere -# with import machinery -import test.ann_module as ann_module -import typing -from collections import ChainMap -from test import ann_module2 -import test - -# These are shared with test_tokenize and other test modules. -# -# Note: since several test cases filter out floats by looking for "e" and ".", -# don't add hexadecimal literals that contain "e" or "E". -VALID_UNDERSCORE_LITERALS = [ - '0_0_0', - '4_2', - '1_0000_0000', - '0b1001_0100', - '0xffff_ffff', - '0o5_7_7', - '1_00_00.5', - '1_00_00.5e5', - '1_00_00e5_1', - '1e1_0', - '.1_4', - '.1_4e1', - '0b_0', - '0x_f', - '0o_5', - '1_00_00j', - '1_00_00.5j', - '1_00_00e5_1j', - '.1_4j', - '(1_2.5+3_3j)', - '(.5_6j)', -] -INVALID_UNDERSCORE_LITERALS = [ - # Trailing underscores: - '0_', - '42_', - '1.4j_', - '0x_', - '0b1_', - '0xf_', - '0o5_', - '0 if 1_Else 1', - # Underscores in the base selector: - '0_b0', - '0_xf', - '0_o5', - # Old-style octal, still disallowed: - '0_7', - '09_99', - # Multiple consecutive underscores: - '4_______2', - '0.1__4', - '0.1__4j', - '0b1001__0100', - '0xffff__ffff', - '0x___', - '0o5__77', - '1e1__0', - '1e1__0j', - # Underscore right before a dot: - '1_.4', - '1_.4j', - # Underscore right after a dot: - '1._4', - '1._4j', - '._5', - '._5j', - # Underscore right after a sign: - '1.0e+_1', - '1.0e+_1j', - # Underscore right before j: - '1.4_j', - '1.4e5_j', - # Underscore right before e: - '1_e1', - '1.4_e1', - '1.4_e1j', - # Underscore right after e: - '1e_1', - '1.4e_1', - '1.4e_1j', - # Complex cases with parens: - '(1+1.5_j_)', - '(1+1.5_j)', -] - - -class TokenTests(unittest.TestCase): - - def test_backslash(self): - # Backslash means line continuation: - x = 1 \ - + 1 - self.assertEqual(x, 2, 'backslash for line continuation') - - # Backslash does not means continuation in comments :\ - x = 0 - self.assertEqual(x, 0, 'backslash ending comment') - - def test_plain_integers(self): - self.assertEqual(type(000), type(0)) - self.assertEqual(0xff, 255) - self.assertEqual(0o377, 255) - self.assertEqual(2147483647, 0o17777777777) - self.assertEqual(0b1001, 9) - # "0x" is not a valid literal - self.assertRaises(SyntaxError, eval, "0x") - from sys import maxsize - if maxsize == 2147483647: - self.assertEqual(-2147483647 - 1, -0o20000000000) - # XXX -2147483648 - self.assertTrue(0o37777777777 > 0) - self.assertTrue(0xffffffff > 0) - self.assertTrue(0b1111111111111111111111111111111 > 0) - for s in ('2147483648', '0o40000000000', '0x100000000', - '0b10000000000000000000000000000000'): - try: - x = eval(s) - except OverflowError: - self.fail("OverflowError on huge integer literal %r" % s) - elif maxsize == 9223372036854775807: - self.assertEqual(-9223372036854775807 - 1, -0o1000000000000000000000) - self.assertTrue(0o1777777777777777777777 > 0) - self.assertTrue(0xffffffffffffffff > 0) - self.assertTrue(0b11111111111111111111111111111111111111111111111111111111111111 > 0) - for s in '9223372036854775808', '0o2000000000000000000000', \ - '0x10000000000000000', \ - '0b100000000000000000000000000000000000000000000000000000000000000': - try: - x = eval(s) - except OverflowError: - self.fail("OverflowError on huge integer literal %r" % s) - else: - self.fail('Weird maxsize value %r' % maxsize) - - def test_long_integers(self): - x = 0 - x = 0xffffffffffffffff - x = 0Xffffffffffffffff - x = 0o77777777777777777 - x = 0O77777777777777777 - x = 123456789012345678901234567890 - x = 0b100000000000000000000000000000000000000000000000000000000000000000000 - x = 0B111111111111111111111111111111111111111111111111111111111111111111111 - - def test_floats(self): - x = 3.14 - x = 314. - x = 0.314 - # XXX x = 000.314 - x = .314 - x = 3e14 - x = 3E14 - x = 3e-14 - x = 3e+14 - x = 3.e14 - x = .3e14 - x = 3.1e4 - - def test_float_exponent_tokenization(self): - # See issue 21642. - self.assertEqual(1 if 1 else 0, 1) - self.assertEqual(1 if 0 else 0, 0) - self.assertRaises(SyntaxError, eval, "0 if 1Else 0") - - def test_underscore_literals(self): - for lit in VALID_UNDERSCORE_LITERALS: - self.assertEqual(eval(lit), eval(lit.replace('_', ''))) - for lit in INVALID_UNDERSCORE_LITERALS: - self.assertRaises(SyntaxError, eval, lit) - # Sanity check: no literal begins with an underscore - self.assertRaises(NameError, eval, "_0") - - def test_string_literals(self): - x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y) - x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39) - x = '"'; y = "\""; self.assertTrue(len(x) == 1 and x == y and ord(x) == 34) - x = "doesn't \"shrink\" does it" - y = 'doesn\'t "shrink" does it' - self.assertTrue(len(x) == 24 and x == y) - x = "does \"shrink\" doesn't it" - y = 'does "shrink" doesn\'t it' - self.assertTrue(len(x) == 24 and x == y) - x = """ -The "quick" -brown fox -jumps over -the 'lazy' dog. -""" - y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n' - self.assertEqual(x, y) - y = ''' -The "quick" -brown fox -jumps over -the 'lazy' dog. -''' - self.assertEqual(x, y) - y = "\n\ -The \"quick\"\n\ -brown fox\n\ -jumps over\n\ -the 'lazy' dog.\n\ -" - self.assertEqual(x, y) - y = '\n\ -The \"quick\"\n\ -brown fox\n\ -jumps over\n\ -the \'lazy\' dog.\n\ -' - self.assertEqual(x, y) - - def test_ellipsis(self): - x = ... - self.assertTrue(x is Ellipsis) - self.assertRaises(SyntaxError, eval, ".. .") - - def test_eof_error(self): - samples = ("def foo(", "\ndef foo(", "def foo(\n") - for s in samples: - with self.assertRaises(SyntaxError) as cm: - compile(s, "", "exec") - self.assertIn("unexpected EOF", str(cm.exception)) - -var_annot_global: int # a global annotated is necessary for test_var_annot - -# custom namespace for testing __annotations__ - -class CNS: - def __init__(self): - self._dct = {} - def __setitem__(self, item, value): - self._dct[item.lower()] = value - def __getitem__(self, item): - return self._dct[item] - - -class GrammarTests(unittest.TestCase): - - check_syntax_error = check_syntax_error - - # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE - # XXX can't test in a script -- this rule is only used when interactive - - # file_input: (NEWLINE | stmt)* ENDMARKER - # Being tested as this very moment this very module - - # expr_input: testlist NEWLINE - # XXX Hard to test -- used only in calls to input() - - def test_eval_input(self): - # testlist ENDMARKER - x = eval('1, 0 or 1') - - def test_var_annot_basics(self): - # all these should be allowed - var1: int = 5 - var2: [int, str] - my_lst = [42] - def one(): - return 1 - int.new_attr: int - [list][0]: type - my_lst[one() - 1]: int = 5 - self.assertEqual(my_lst, [5]) - - def test_var_annot_syntax_errors(self): - # parser pass - check_syntax_error(self, "def f: int") - check_syntax_error(self, "x: int: str") - check_syntax_error(self, "def f():\n" - " nonlocal x: int\n") - # AST pass - check_syntax_error(self, "[x, 0]: int\n") - check_syntax_error(self, "f(): int\n") - check_syntax_error(self, "(x,): int") - check_syntax_error(self, "def f():\n" - " (x, y): int = (1, 2)\n") - # symtable pass - check_syntax_error(self, "def f():\n" - " x: int\n" - " global x\n") - check_syntax_error(self, "def f():\n" - " global x\n" - " x: int\n") - - def test_var_annot_basic_semantics(self): - # execution order - with self.assertRaises(ZeroDivisionError): - no_name[does_not_exist]: no_name_again = 1 / 0 - with self.assertRaises(NameError): - no_name[does_not_exist]: 1 / 0 = 0 - global var_annot_global - - # function semantics - def f(): - st: str = "Hello" - a.b: int = (1, 2) - return st - self.assertEqual(f.__annotations__, {}) - def f_OK(): - x: 1 / 0 - f_OK() - def fbad(): - x: int - print(x) - with self.assertRaises(UnboundLocalError): - fbad() - def f2bad(): - (no_such_global): int - print(no_such_global) - try: - f2bad() - except Exception as e: - self.assertIs(type(e), NameError) - - # class semantics - class C: - __foo: int - s: str = "attr" - z = 2 - def __init__(self, x): - self.x: int = x - self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str}) - with self.assertRaises(NameError): - class CBad: - no_such_name_defined.attr: int = 0 - with self.assertRaises(NameError): - class Cbad2(C): - x: int - x.y: list = [] - - def test_var_annot_metaclass_semantics(self): - class CMeta(type): - @classmethod - def __prepare__(metacls, name, bases, **kwds): - return {'__annotations__': CNS()} - class CC(metaclass=CMeta): - XX: 'ANNOT' - self.assertEqual(CC.__annotations__['xx'], 'ANNOT') - - def test_var_annot_module_semantics(self): - with self.assertRaises(AttributeError): - print(test.__annotations__) - self.assertEqual(ann_module.__annotations__, - {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]}) - self.assertEqual(ann_module.M.__annotations__, - {'123': 123, 'o': type}) - self.assertEqual(ann_module2.__annotations__, {}) - - def test_var_annot_in_module(self): - # check that functions fail the same way when executed - # outside of module where they were defined - from test.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann - with self.assertRaises(NameError): - f_bad_ann() - with self.assertRaises(NameError): - g_bad_ann() - with self.assertRaises(NameError): - D_bad_ann(5) - - def test_var_annot_simple_exec(self): - gns = {}; lns = {} - exec("'docstring'\n" - "__annotations__[1] = 2\n" - "x: int = 5\n", gns, lns) - self.assertEqual(lns["__annotations__"], {1: 2, 'x': int}) - with self.assertRaises(KeyError): - gns['__annotations__'] - - def test_var_annot_custom_maps(self): - # tests with custom locals() and __annotations__ - ns = {'__annotations__': CNS()} - exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) - self.assertEqual(ns['__annotations__']['x'], int) - self.assertEqual(ns['__annotations__']['z'], str) - with self.assertRaises(KeyError): - ns['__annotations__']['w'] - nonloc_ns = {} - class CNS2: - def __init__(self): - self._dct = {} - def __setitem__(self, item, value): - nonlocal nonloc_ns - self._dct[item] = value - nonloc_ns[item] = value - def __getitem__(self, item): - return self._dct[item] - exec('x: int = 1', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], int) - - def test_var_annot_refleak(self): - # complex case: custom locals plus custom __annotations__ - # this was causing refleak - cns = CNS() - nonloc_ns = {'__annotations__': cns} - class CNS2: - def __init__(self): - self._dct = {'__annotations__': cns} - def __setitem__(self, item, value): - nonlocal nonloc_ns - self._dct[item] = value - nonloc_ns[item] = value - def __getitem__(self, item): - return self._dct[item] - exec('X: str', {}, CNS2()) - self.assertEqual(nonloc_ns['__annotations__']['x'], str) - - def test_funcdef(self): - ### [decorators] 'def' NAME parameters ['->' test] ':' suite - ### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE - ### decorators: decorator+ - ### parameters: '(' [typedargslist] ')' - ### typedargslist: ((tfpdef ['=' test] ',')* - ### ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) - ### | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) - ### tfpdef: NAME [':' test] - ### varargslist: ((vfpdef ['=' test] ',')* - ### ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) - ### | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) - ### vfpdef: NAME - def f1(): pass - f1() - f1(*()) - f1(*(), **{}) - def f2(one_argument): pass - def f3(two, arguments): pass - self.assertEqual(f2.__code__.co_varnames, ('one_argument',)) - self.assertEqual(f3.__code__.co_varnames, ('two', 'arguments')) - def a1(one_arg,): pass - def a2(two, args,): pass - def v0(*rest): pass - def v1(a, *rest): pass - def v2(a, b, *rest): pass - - f1() - f2(1) - f2(1,) - f3(1, 2) - f3(1, 2,) - v0() - v0(1) - v0(1,) - v0(1, 2) - v0(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) - v1(1) - v1(1,) - v1(1, 2) - v1(1, 2, 3) - v1(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) - v2(1, 2) - v2(1, 2, 3) - v2(1, 2, 3, 4) - v2(1, 2, 3, 4, 5, 6, 7, 8, 9, 0) - - def d01(a=1): pass - d01() - d01(1) - d01(*(1,)) - d01(*[] or [2]) - d01(*() or (), *{} and (), **() or {}) - d01(**{'a': 2}) - d01(**{'a': 2} or {}) - def d11(a, b=1): pass - d11(1) - d11(1, 2) - d11(1, **{'b': 2}) - def d21(a, b, c=1): pass - d21(1, 2) - d21(1, 2, 3) - d21(*(1, 2, 3)) - d21(1, *(2, 3)) - d21(1, 2, *(3,)) - d21(1, 2, **{'c': 3}) - def d02(a=1, b=2): pass - d02() - d02(1) - d02(1, 2) - d02(*(1, 2)) - d02(1, *(2,)) - d02(1, **{'b': 2}) - d02(**{'a': 1, 'b': 2}) - def d12(a, b=1, c=2): pass - d12(1) - d12(1, 2) - d12(1, 2, 3) - def d22(a, b, c=1, d=2): pass - d22(1, 2) - d22(1, 2, 3) - d22(1, 2, 3, 4) - def d01v(a=1, *rest): pass - d01v() - d01v(1) - d01v(1, 2) - d01v(*(1, 2, 3, 4)) - d01v(*(1,)) - d01v(**{'a': 2}) - def d11v(a, b=1, *rest): pass - d11v(1) - d11v(1, 2) - d11v(1, 2, 3) - def d21v(a, b, c=1, *rest): pass - d21v(1, 2) - d21v(1, 2, 3) - d21v(1, 2, 3, 4) - d21v(*(1, 2, 3, 4)) - d21v(1, 2, **{'c': 3}) - def d02v(a=1, b=2, *rest): pass - d02v() - d02v(1) - d02v(1, 2) - d02v(1, 2, 3) - d02v(1, *(2, 3, 4)) - d02v(**{'a': 1, 'b': 2}) - def d12v(a, b=1, c=2, *rest): pass - d12v(1) - d12v(1, 2) - d12v(1, 2, 3) - d12v(1, 2, 3, 4) - d12v(*(1, 2, 3, 4)) - d12v(1, 2, *(3, 4, 5)) - d12v(1, *(2,), **{'c': 3}) - def d22v(a, b, c=1, d=2, *rest): pass - d22v(1, 2) - d22v(1, 2, 3) - d22v(1, 2, 3, 4) - d22v(1, 2, 3, 4, 5) - d22v(*(1, 2, 3, 4)) - d22v(1, 2, *(3, 4, 5)) - d22v(1, *(2, 3), **{'d': 4}) - - # keyword argument type tests - try: - str('x', **{b'foo': 1}) - except TypeError: - pass - else: - self.fail('Bytes should not work as keyword argument names') - # keyword only argument tests - def pos0key1(*, key): return key - pos0key1(key=100) - def pos2key2(p1, p2, *, k1, k2=100): return p1, p2, k1, k2 - pos2key2(1, 2, k1=100) - pos2key2(1, 2, k1=100, k2=200) - pos2key2(1, 2, k2=100, k1=200) - def pos2key2dict(p1, p2, *, k1=100, k2, **kwarg): return p1, p2, k1, k2, kwarg - pos2key2dict(1, 2, k2=100, tokwarg1=100, tokwarg2=200) - pos2key2dict(1, 2, tokwarg1=100, tokwarg2=200, k2=100) - - self.assertRaises(SyntaxError, eval, "def f(*): pass") - self.assertRaises(SyntaxError, eval, "def f(*,): pass") - self.assertRaises(SyntaxError, eval, "def f(*, **kwds): pass") - - # keyword arguments after *arglist - def f(*args, **kwargs): - return args, kwargs - self.assertEqual(f(1, x=2, *[3, 4], y=5), ((1, 3, 4), - {'x': 2, 'y': 5})) - self.assertEqual(f(1, *(2, 3), 4), ((1, 2, 3, 4), {})) - self.assertRaises(SyntaxError, eval, "f(1, x=2, *(3,4), x=5)") - self.assertEqual(f(**{'eggs': 'scrambled', 'spam': 'fried'}), - ((), {'eggs': 'scrambled', 'spam': 'fried'})) - self.assertEqual(f(spam='fried', **{'eggs': 'scrambled'}), - ((), {'eggs': 'scrambled', 'spam': 'fried'})) - - # Check ast errors in *args and *kwargs - check_syntax_error(self, "f(*g(1=2))") - check_syntax_error(self, "f(**g(1=2))") - - # argument annotation tests - def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': list}) - def f(x: int): pass - self.assertEqual(f.__annotations__, {'x': int}) - def f(*x: str): pass - self.assertEqual(f.__annotations__, {'x': str}) - def f(**x: float): pass - self.assertEqual(f.__annotations__, {'x': float}) - def f(x, y: 1 + 2): pass - self.assertEqual(f.__annotations__, {'y': 3}) - def f(a, b: 1, c: 2, d): pass - self.assertEqual(f.__annotations__, {'b': 1, 'c': 2}) - def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6): pass - self.assertEqual(f.__annotations__, - {'b': 1, 'c': 2, 'e': 3, 'g': 6}) - def f(a, b: 1, c: 2, d, e: 3 = 4, f=5, *g: 6, h: 7, i=8, j: 9 = 10, - **k: 11) -> 12: pass - self.assertEqual(f.__annotations__, - {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9, - 'k': 11, 'return': 12}) - # Check for issue #20625 -- annotations mangling - class Spam: - def f(self, *, __kw: 1): - pass - class Ham(Spam): pass - self.assertEqual(Spam.f.__annotations__, {'_Spam__kw': 1}) - self.assertEqual(Ham.f.__annotations__, {'_Spam__kw': 1}) - # Check for SF Bug #1697248 - mixing decorators and a return annotation - def null(x): return x - @null - def f(x) -> list: pass - self.assertEqual(f.__annotations__, {'return': list}) - - # test closures with a variety of opargs - closure = 1 - def f(): return closure - def f(x=1): return closure - def f(*, k=1): return closure - def f() -> int: return closure - - # Check trailing commas are permitted in funcdef argument list - def f(a,): pass - def f(*args,): pass - def f(**kwds,): pass - def f(a, *args,): pass - def f(a, **kwds,): pass - def f(*args, b,): pass - def f(*, b,): pass - def f(*args, **kwds,): pass - def f(a, *args, b,): pass - def f(a, *, b,): pass - def f(a, *args, **kwds,): pass - def f(*args, b, **kwds,): pass - def f(*, b, **kwds,): pass - def f(a, *args, b, **kwds,): pass - def f(a, *, b, **kwds,): pass - - def test_lambdef(self): - ### lambdef: 'lambda' [varargslist] ':' test - l1 = lambda: 0 - self.assertEqual(l1(), 0) - l2 = lambda: a[d] # XXX just testing the expression - l3 = lambda: [2 < x for x in [-1, 3, 0]] - self.assertEqual(l3(), [0, 1, 0]) - l4 = lambda x=lambda y=lambda z=1: z: y(): x() - self.assertEqual(l4(), 1) - l5 = lambda x, y, z=2: x + y + z - self.assertEqual(l5(1, 2), 5) - self.assertEqual(l5(1, 2, 3), 6) - check_syntax_error(self, "lambda x: x = 2") - check_syntax_error(self, "lambda (None,): None") - l6 = lambda x, y, *, k=20: x + y + k - self.assertEqual(l6(1, 2), 1 + 2 + 20) - self.assertEqual(l6(1, 2, k=10), 1 + 2 + 10) - - # check that trailing commas are permitted - l10 = lambda a,: 0 - l11 = lambda *args,: 0 - l12 = lambda **kwds,: 0 - l13 = lambda a, *args,: 0 - l14 = lambda a, **kwds,: 0 - l15 = lambda *args, b,: 0 - l16 = lambda *, b,: 0 - l17 = lambda *args, **kwds,: 0 - l18 = lambda a, *args, b,: 0 - l19 = lambda a, *, b,: 0 - l20 = lambda a, *args, **kwds,: 0 - l21 = lambda *args, b, **kwds,: 0 - l22 = lambda *, b, **kwds,: 0 - l23 = lambda a, *args, b, **kwds,: 0 - l24 = lambda a, *, b, **kwds,: 0 - - - ### stmt: simple_stmt | compound_stmt - # Tested below - - def test_simple_stmt(self): - ### simple_stmt: small_stmt (';' small_stmt)* [';'] - x = 1; pass; del x - def foo(): - # verify statements that end with semi-colons - x = 1; pass; del x; - foo() - - ### small_stmt: expr_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt - # Tested below - - def test_expr_stmt(self): - # (exprlist '=')* exprlist - 1 - 1, 2, 3 - x = 1 - x = 1, 2, 3 - x = y = z = 1, 2, 3 - x, y, z = 1, 2, 3 - abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4) - - check_syntax_error(self, "x + 1 = 1") - check_syntax_error(self, "a + 1 = b + 2") - - # Check the heuristic for print & exec covers significant cases - # As well as placing some limits on false positives - def test_former_statements_refer_to_builtins(self): - keywords = "print", "exec" - # Cases where we want the custom error - cases = [ - "{} foo", - "{} {{1:foo}}", - "if 1: {} foo", - "if 1: {} {{1:foo}}", - "if 1:\n {} foo", - "if 1:\n {} {{1:foo}}", - ] - for keyword in keywords: - custom_msg = "call to '{}'".format(keyword) - for case in cases: - source = case.format(keyword) - with self.subTest(source=source): - with self.assertRaisesRegex(SyntaxError, custom_msg): - exec(source) - source = source.replace("foo", "(foo.)") - with self.subTest(source=source): - with self.assertRaisesRegex(SyntaxError, "invalid syntax"): - exec(source) - - def test_del_stmt(self): - # 'del' exprlist - abc = [1, 2, 3] - x, y, z = abc - xyz = x, y, z - - del abc - del x, y, (z, xyz) - - def test_pass_stmt(self): - # 'pass' - pass - - # flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt - # Tested below - - def test_break_stmt(self): - # 'break' - while 1: break - - def test_continue_stmt(self): - # 'continue' - i = 1 - while i: i = 0; continue - - msg = "" - while not msg: - msg = "ok" - try: - continue - msg = "continue failed to continue inside try" - except: - msg = "continue inside try called except block" - if msg != "ok": - self.fail(msg) - - msg = "" - while not msg: - msg = "finally block not called" - try: - continue - finally: - msg = "ok" - if msg != "ok": - self.fail(msg) - - def test_break_continue_loop(self): - # This test warrants an explanation. It is a test specifically for SF bugs - # #463359 and #462937. The bug is that a 'break' statement executed or - # exception raised inside a try/except inside a loop, *after* a continue - # statement has been executed in that loop, will cause the wrong number of - # arguments to be popped off the stack and the instruction pointer reset to - # a very small number (usually 0.) Because of this, the following test - # *must* written as a function, and the tracking vars *must* be function - # arguments with default values. Otherwise, the test will loop and loop. - - def test_inner(extra_burning_oil=1, count=0): - big_hippo = 2 - while big_hippo: - count += 1 - try: - if extra_burning_oil and big_hippo == 1: - extra_burning_oil -= 1 - break - big_hippo -= 1 - continue - except: - raise - if count > 2 or big_hippo != 1: - self.fail("continue then break in try/except in loop broken!") - test_inner() - - def test_return(self): - # 'return' [testlist] - def g1(): return - def g2(): return 1 - g1() - x = g2() - check_syntax_error(self, "class foo:return 1") - - def test_break_in_finally(self): - count = 0 - while count < 2: - count += 1 - try: - pass - finally: - break - self.assertEqual(count, 1) - - count = 0 - while count < 2: - count += 1 - try: - continue - finally: - break - self.assertEqual(count, 1) - - count = 0 - while count < 2: - count += 1 - try: - 1 / 0 - finally: - break - self.assertEqual(count, 1) - - for count in [0, 1]: - self.assertEqual(count, 0) - try: - pass - finally: - break - self.assertEqual(count, 0) - - for count in [0, 1]: - self.assertEqual(count, 0) - try: - continue - finally: - break - self.assertEqual(count, 0) - - for count in [0, 1]: - self.assertEqual(count, 0) - try: - 1 / 0 - finally: - break - self.assertEqual(count, 0) - - def test_continue_in_finally(self): - count = 0 - while count < 2: - count += 1 - try: - pass - finally: - continue - break - self.assertEqual(count, 2) - - count = 0 - while count < 2: - count += 1 - try: - break - finally: - continue - self.assertEqual(count, 2) - - count = 0 - while count < 2: - count += 1 - try: - 1 / 0 - finally: - continue - break - self.assertEqual(count, 2) - - for count in [0, 1]: - try: - pass - finally: - continue - break - self.assertEqual(count, 1) - - for count in [0, 1]: - try: - break - finally: - continue - self.assertEqual(count, 1) - - for count in [0, 1]: - try: - 1 / 0 - finally: - continue - break - self.assertEqual(count, 1) - - def test_return_in_finally(self): - def g1(): - try: - pass - finally: - return 1 - self.assertEqual(g1(), 1) - - def g2(): - try: - return 2 - finally: - return 3 - self.assertEqual(g2(), 3) - - def g3(): - try: - 1 / 0 - finally: - return 4 - self.assertEqual(g3(), 4) - - def test_yield(self): - # Allowed as standalone statement - def g(): yield 1 - def g(): yield from () - # Allowed as RHS of assignment - def g(): x = yield 1 - def g(): x = yield from () - # Ordinary yield accepts implicit tuples - def g(): yield 1, 1 - def g(): x = yield 1, 1 - # 'yield from' does not - check_syntax_error(self, "def g(): yield from (), 1") - check_syntax_error(self, "def g(): x = yield from (), 1") - # Requires parentheses as subexpression - def g(): 1, (yield 1) - def g(): 1, (yield from ()) - check_syntax_error(self, "def g(): 1, yield 1") - check_syntax_error(self, "def g(): 1, yield from ()") - # Requires parentheses as call argument - def g(): f((yield 1)) - def g(): f((yield 1), 1) - def g(): f((yield from ())) - def g(): f((yield from ()), 1) - check_syntax_error(self, "def g(): f(yield 1)") - check_syntax_error(self, "def g(): f(yield 1, 1)") - check_syntax_error(self, "def g(): f(yield from ())") - check_syntax_error(self, "def g(): f(yield from (), 1)") - # Not allowed at top level - check_syntax_error(self, "yield") - check_syntax_error(self, "yield from") - # Not allowed at class scope - check_syntax_error(self, "class foo:yield 1") - check_syntax_error(self, "class foo:yield from ()") - # Check annotation refleak on SyntaxError - check_syntax_error(self, "def g(a:(yield)): pass") - - def test_yield_in_comprehensions(self): - # Check yield in comprehensions - def g(): [x for x in [(yield 1)]] - def g(): [x for x in [(yield from ())]] - - check = self.check_syntax_error - check("def g(): [(yield x) for x in ()]", - "'yield' inside list comprehension") - check("def g(): [x for x in () if not (yield x)]", - "'yield' inside list comprehension") - check("def g(): [y for x in () for y in [(yield x)]]", - "'yield' inside list comprehension") - check("def g(): {(yield x) for x in ()}", - "'yield' inside set comprehension") - check("def g(): {(yield x): x for x in ()}", - "'yield' inside dict comprehension") - check("def g(): {x: (yield x) for x in ()}", - "'yield' inside dict comprehension") - check("def g(): ((yield x) for x in ())", - "'yield' inside generator expression") - check("def g(): [(yield from x) for x in ()]", - "'yield' inside list comprehension") - check("class C: [(yield x) for x in ()]", - "'yield' inside list comprehension") - check("[(yield x) for x in ()]", - "'yield' inside list comprehension") - - def test_raise(self): - # 'raise' test [',' test] - try: raise RuntimeError('just testing') - except RuntimeError: pass - try: raise KeyboardInterrupt - except KeyboardInterrupt: pass - - def test_import(self): - # 'import' dotted_as_names - import sys - import time, sys - # 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names) - from time import time - from time import (time) - # not testable inside a function, but already done at top of the module - # from sys import * - from sys import path, argv - from sys import (path, argv) - from sys import (path, argv,) - - def test_global(self): - # 'global' NAME (',' NAME)* - global a - global a, b - global one, two, three, four, five, six, seven, eight, nine, ten - - def test_nonlocal(self): - # 'nonlocal' NAME (',' NAME)* - x = 0 - y = 0 - def f(): - nonlocal x - nonlocal x, y - - def test_assert(self): - # assertTruestmt: 'assert' test [',' test] - assert 1 - assert 1, 1 - assert lambda x: x - assert 1, lambda x: x + 1 - - try: - assert True - except AssertionError as e: - self.fail("'assert True' should not have raised an AssertionError") - - try: - assert True, 'this should always pass' - except AssertionError as e: - self.fail("'assert True, msg' should not have " - "raised an AssertionError") - - # these tests fail if python is run with -O, so check __debug__ - @unittest.skipUnless(__debug__, "Won't work if __debug__ is False") - def testAssert2(self): - try: - assert 0, "msg" - except AssertionError as e: - self.assertEqual(e.args[0], "msg") - else: - self.fail("AssertionError not raised by assert 0") - - try: - assert False - except AssertionError as e: - self.assertEqual(len(e.args), 0) - else: - self.fail("AssertionError not raised by 'assert False'") - - - ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef - # Tested below - - def test_if(self): - # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] - if 1: pass - if 1: pass - else: pass - if 0: pass - elif 0: pass - if 0: pass - elif 0: pass - elif 0: pass - elif 0: pass - else: pass - - def test_while(self): - # 'while' test ':' suite ['else' ':' suite] - while 0: pass - while 0: pass - else: pass - - # Issue1920: "while 0" is optimized away, - # ensure that the "else" clause is still present. - x = 0 - while 0: - x = 1 - else: - x = 2 - self.assertEqual(x, 2) - - def test_for(self): - # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] - for i in 1, 2, 3: pass - for i, j, k in (): pass - else: pass - class Squares: - def __init__(self, max): - self.max = max - self.sofar = [] - def __len__(self): return len(self.sofar) - def __getitem__(self, i): - if not 0 <= i < self.max: raise IndexError - n = len(self.sofar) - while n <= i: - self.sofar.append(n * n) - n = n + 1 - return self.sofar[i] - n = 0 - for x in Squares(10): n = n + x - if n != 285: - self.fail('for over growing sequence') - - result = [] - for x, in [(1,), (2,), (3,)]: - result.append(x) - self.assertEqual(result, [1, 2, 3]) - - def test_try(self): - ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] - ### | 'try' ':' suite 'finally' ':' suite - ### except_clause: 'except' [expr ['as' expr]] - try: - 1 / 0 - except ZeroDivisionError: - pass - else: - pass - try: 1 / 0 - except EOFError: pass - except TypeError as msg: pass - except: pass - else: pass - try: 1 / 0 - except (EOFError, TypeError, ZeroDivisionError): pass - try: 1 / 0 - except (EOFError, TypeError, ZeroDivisionError) as msg: pass - try: pass - finally: pass - - def test_suite(self): - # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT - if 1: pass - if 1: - pass - if 1: - # - # - # - pass - pass - # - pass - # - - def test_test(self): - ### and_test ('or' and_test)* - ### and_test: not_test ('and' not_test)* - ### not_test: 'not' not_test | comparison - if not 1: pass - if 1 and 1: pass - if 1 or 1: pass - if not not not 1: pass - if not 1 and 1 and 1: pass - if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass - - def test_comparison(self): - ### comparison: expr (comp_op expr)* - ### comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not' - if 1: pass - x = (1 == 1) - if 1 == 1: pass - if 1 != 1: pass - if 1 < 1: pass - if 1 > 1: pass - if 1 <= 1: pass - if 1 >= 1: pass - if 1 is 1: pass - if 1 is not 1: pass - if 1 in (): pass - if 1 not in (): pass - if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in 1 is 1 is not 1: pass - - def test_binary_mask_ops(self): - x = 1 & 1 - x = 1 ^ 1 - x = 1 | 1 - - def test_shift_ops(self): - x = 1 << 1 - x = 1 >> 1 - x = 1 << 1 >> 1 - - def test_additive_ops(self): - x = 1 - x = 1 + 1 - x = 1 - 1 - 1 - x = 1 - 1 + 1 - 1 + 1 - - def test_multiplicative_ops(self): - x = 1 * 1 - x = 1 / 1 - x = 1 % 1 - x = 1 / 1 * 1 % 1 - - def test_unary_ops(self): - x = +1 - x = -1 - x = ~1 - x = ~1 ^ 1 & 1 | 1 & 1 ^ -1 - x = -1 * 1 / 1 + 1 * 1 - -1 * 1 - - def test_selectors(self): - ### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME - ### subscript: expr | [expr] ':' [expr] - - import sys, time - c = sys.path[0] - x = time.time() - x = sys.modules['time'].time() - a = '01234' - c = a[0] - c = a[-1] - s = a[0:5] - s = a[:5] - s = a[0:] - s = a[:] - s = a[-5:] - s = a[:-1] - s = a[-4:-3] - # A rough test of SF bug 1333982. http://python.org/sf/1333982 - # The testing here is fairly incomplete. - # Test cases should include: commas with 1 and 2 colons - d = {} - d[1] = 1 - d[1,] = 2 - d[1, 2] = 3 - d[1, 2, 3] = 4 - L = list(d) - L.sort(key=lambda x: (type(x).__name__, x)) - self.assertEqual(str(L), '[1, (1,), (1, 2), (1, 2, 3)]') - - def test_atoms(self): - ### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictsetmaker] '}' | NAME | NUMBER | STRING - ### dictsetmaker: (test ':' test (',' test ':' test)* [',']) | (test (',' test)* [',']) - - x = (1) - x = (1 or 2 or 3) - x = (1 or 2 or 3, 2, 3) - - x = [] - x = [1] - x = [1 or 2 or 3] - x = [1 or 2 or 3, 2, 3] - x = [] - - x = {} - x = {'one': 1} - x = {'one': 1,} - x = {'one' or 'two': 1 or 2} - x = {'one': 1, 'two': 2} - x = {'one': 1, 'two': 2,} - x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6} - - x = {'one'} - x = {'one', 1,} - x = {'one', 'two', 'three'} - x = {2, 3, 4,} - - x = x - x = 'x' - x = 123 - - ### exprlist: expr (',' expr)* [','] - ### testlist: test (',' test)* [','] - # These have been exercised enough above - - def test_classdef(self): - # 'class' NAME ['(' [testlist] ')'] ':' suite - class B: pass - class B2(): pass - class C1(B): pass - class C2(B): pass - class D(C1, C2, B): pass - class C: - def meth1(self): pass - def meth2(self, arg): pass - def meth3(self, a1, a2): pass - - # decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE - # decorators: decorator+ - # decorated: decorators (classdef | funcdef) - def class_decorator(x): return x - @class_decorator - class G: pass - - def test_dictcomps(self): - # dictorsetmaker: ( (test ':' test (comp_for | - # (',' test ':' test)* [','])) | - # (test (comp_for | (',' test)* [','])) ) - nums = [1, 2, 3] - self.assertEqual({i: i + 1 for i in nums}, {1: 2, 2: 3, 3: 4}) - - def test_listcomps(self): - # list comprehension tests - nums = [1, 2, 3, 4, 5] - strs = ["Apple", "Banana", "Coconut"] - spcs = [" Apple", " Banana ", "Coco nut "] - - self.assertEqual([s.strip() for s in spcs], ['Apple', 'Banana', 'Coco nut']) - self.assertEqual([3 * x for x in nums], [3, 6, 9, 12, 15]) - self.assertEqual([x for x in nums if x > 2], [3, 4, 5]) - self.assertEqual([(i, s) for i in nums for s in strs], - [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'), - (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'), - (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'), - (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'), - (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')]) - self.assertEqual([(i, s) for i in nums for s in [f for f in strs if "n" in f]], - [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'), - (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'), - (5, 'Banana'), (5, 'Coconut')]) - self.assertEqual([(lambda a:[a ** i for i in range(a + 1)])(j) for j in range(5)], - [[1], [1, 1], [1, 2, 4], [1, 3, 9, 27], [1, 4, 16, 64, 256]]) - - def test_in_func(l): - return [0 < x < 3 for x in l if x > 2] - - self.assertEqual(test_in_func(nums), [False, False, False]) - - def test_nested_front(): - self.assertEqual([[y for y in [x, x + 1]] for x in [1, 3, 5]], - [[1, 2], [3, 4], [5, 6]]) - - test_nested_front() - - check_syntax_error(self, "[i, s for i in nums for s in strs]") - check_syntax_error(self, "[x if y]") - - suppliers = [ - (1, "Boeing"), - (2, "Ford"), - (3, "Macdonalds") - ] - - parts = [ - (10, "Airliner"), - (20, "Engine"), - (30, "Cheeseburger") - ] - - suppart = [ - (1, 10), (1, 20), (2, 20), (3, 30) - ] - - x = [ - (sname, pname) - for (sno, sname) in suppliers - for (pno, pname) in parts - for (sp_sno, sp_pno) in suppart - if sno == sp_sno and pno == sp_pno - ] - - self.assertEqual(x, [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'), - ('Macdonalds', 'Cheeseburger')]) - - def test_genexps(self): - # generator expression tests - g = ([x for x in range(10)] for x in range(1)) - self.assertEqual(next(g), [x for x in range(10)]) - try: - next(g) - self.fail('should produce StopIteration exception') - except StopIteration: - pass - - a = 1 - try: - g = (a for d in a) - next(g) - self.fail('should produce TypeError') - except TypeError: - pass - - self.assertEqual(list((x, y) for x in 'abcd' for y in 'abcd'), [(x, y) for x in 'abcd' for y in 'abcd']) - self.assertEqual(list((x, y) for x in 'ab' for y in 'xy'), [(x, y) for x in 'ab' for y in 'xy']) - - a = [x for x in range(10)] - b = (x for x in (y for y in a)) - self.assertEqual(sum(b), sum([x for x in range(10)])) - - self.assertEqual(sum(x ** 2 for x in range(10)), sum([x ** 2 for x in range(10)])) - self.assertEqual(sum(x * x for x in range(10) if x % 2), sum([x * x for x in range(10) if x % 2])) - self.assertEqual(sum(x for x in (y for y in range(10))), sum([x for x in range(10)])) - self.assertEqual(sum(x for x in (y for y in (z for z in range(10)))), sum([x for x in range(10)])) - self.assertEqual(sum(x for x in [y for y in (z for z in range(10))]), sum([x for x in range(10)])) - self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True)) if True), sum([x for x in range(10)])) - self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True) if False) if True), 0) - check_syntax_error(self, "foo(x for x in range(10), 100)") - check_syntax_error(self, "foo(100, x for x in range(10))") - - def test_comprehension_specials(self): - # test for outmost iterable precomputation - x = 10; g = (i for i in range(x)); x = 5 - self.assertEqual(len(list(g)), 10) - - # This should hold, since we're only precomputing outmost iterable. - x = 10; t = False; g = ((i, j) for i in range(x) if t for j in range(x)) - x = 5; t = True; - self.assertEqual([(i, j) for i in range(10) for j in range(5)], list(g)) - - # Grammar allows multiple adjacent 'if's in listcomps and genexps, - # even though it's silly. Make sure it works (ifelse broke this.) - self.assertEqual([x for x in range(10) if x % 2 if x % 3], [1, 5, 7]) - self.assertEqual(list(x for x in range(10) if x % 2 if x % 3), [1, 5, 7]) - - # verify unpacking single element tuples in listcomp/genexp. - self.assertEqual([x for x, in [(4,), (5,), (6,)]], [4, 5, 6]) - self.assertEqual(list(x for x, in [(7,), (8,), (9,)]), [7, 8, 9]) - - def test_with_statement(self): - class manager(object): - def __enter__(self): - return (1, 2) - def __exit__(self, *args): - pass - - with manager(): - pass - with manager() as x: - pass - with manager() as (x, y): - pass - with manager(), manager(): - pass - with manager() as x, manager() as y: - pass - with manager() as x, manager(): - pass - - def test_if_else_expr(self): - # Test ifelse expressions in various cases - def _checkeval(msg, ret): - "helper to check that evaluation of expressions is done correctly" - print(msg) - return ret - - # the next line is not allowed anymore - #self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True]) - self.assertEqual([x() for x in (lambda:True, lambda:False) if x()], [True]) - self.assertEqual([x(False) for x in (lambda x:False if x else True, lambda x:True if x else False) if x(False)], [True]) - self.assertEqual((5 if 1 else _checkeval("check 1", 0)), 5) - self.assertEqual((_checkeval("check 2", 0) if 0 else 5), 5) - self.assertEqual((5 and 6 if 0 else 1), 1) - self.assertEqual(((5 and 6) if 0 else 1), 1) - self.assertEqual((5 and (6 if 1 else 1)), 6) - self.assertEqual((0 or _checkeval("check 3", 2) if 0 else 3), 3) - self.assertEqual((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)), 1) - self.assertEqual((0 or 5 if 1 else _checkeval("check 6", 3)), 5) - self.assertEqual((not 5 if 1 else 1), False) - self.assertEqual((not 5 if 0 else 1), 1) - self.assertEqual((6 + 1 if 1 else 2), 7) - self.assertEqual((6 - 1 if 1 else 2), 5) - self.assertEqual((6 * 2 if 1 else 4), 12) - self.assertEqual((6 / 2 if 1 else 3), 3) - self.assertEqual((6 < 4 if 0 else 2), 2) - - def test_paren_evaluation(self): - self.assertEqual(16 // (4 // 2), 8) - self.assertEqual((16 // 4) // 2, 2) - self.assertEqual(16 // 4 // 2, 2) - self.assertTrue(False is (2 is 3)) - self.assertFalse((False is 2) is 3) - self.assertFalse(False is 2 is 3) - - def test_matrix_mul(self): - # This is not intended to be a comprehensive test, rather just to be few - # samples of the @ operator in test_grammar.py. - class M: - def __matmul__(self, o): - return 4 - def __imatmul__(self, o): - self.other = o - return self - m = M() - self.assertEqual(m @ m, 4) - m @= 42 - self.assertEqual(m.other, 42) - - def test_async_await(self): - async def test(): - def sum(): - pass - if 1: - await someobj() - - self.assertEqual(test.__name__, 'test') - self.assertTrue(bool(test.__code__.co_flags & inspect.CO_COROUTINE)) - - def decorator(func): - setattr(func, '_marked', True) - return func - - @decorator - async def test2(): - return 22 - self.assertTrue(test2._marked) - self.assertEqual(test2.__name__, 'test2') - self.assertTrue(bool(test2.__code__.co_flags & inspect.CO_COROUTINE)) - - def test_async_for(self): - class Done(Exception): pass - - class AIter: - def __aiter__(self): - return self - async def __anext__(self): - raise StopAsyncIteration - - async def foo(): - async for i in AIter(): - pass - async for i, j in AIter(): - pass - async for i in AIter(): - pass - else: - pass - raise Done - - with self.assertRaises(Done): - foo().send(None) - - def test_async_with(self): - class Done(Exception): pass - - class manager: - async def __aenter__(self): - return (1, 2) - async def __aexit__(self, *exc): - return False - - async def foo(): - async with manager(): - pass - async with manager() as x: - pass - async with manager() as (x, y): - pass - async with manager(), manager(): - pass - async with manager() as x, manager() as y: - pass - async with manager() as x, manager(): - pass - raise Done - - with self.assertRaises(Done): - foo().send(None) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/test/pythonFiles/formatting/yapf.output b/src/test/pythonFiles/formatting/yapf.output deleted file mode 100644 index 43897b1753b0..000000000000 --- a/src/test/pythonFiles/formatting/yapf.output +++ /dev/null @@ -1,57 +0,0 @@ ---- C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py (original) -+++ C:\GIT\issues\s p\vscode-python\src\test\pythonFiles\formatting\fileToformat.py (reformatted) -@@ -1,22 +1,40 @@ -+import math, sys - --import math, sys; - - def example1(): - ####This is a long comment. This should be wrapped to fit within 72 characters. -- some_tuple=( 1,2, 3,'a' ); -- some_variable={'long':'Long code lines should be wrapped within 79 characters.', -- 'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'], -- 'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1, -- 20,300,40000,500000000,60000000000000000]}} -+ some_tuple = (1, 2, 3, 'a') -+ some_variable = { -+ 'long': -+ 'Long code lines should be wrapped within 79 characters.', -+ 'other': [ -+ math.pi, 100, 200, 300, 9876543210, -+ 'This is a long string that goes on' -+ ], -+ 'more': { -+ 'inner': 'This whole logical line should be wrapped.', -+ some_tuple: [1, 20, 300, 40000, 500000000, 60000000000000000] -+ } -+ } - return (some_tuple, some_variable) --def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key('')); --class Example3( object ): -- def __init__ ( self, bar ): -- #Comments should have a space after the hash. -- if bar : bar+=1; bar=bar* bar ; return bar -- else: -- some_string = """ -+ -+ -+def example2(): -+ return { -+ 'has_key() is deprecated': True -+ }.has_key({'f': 2}.has_key('')) -+ -+ -+class Example3(object): -+ def __init__(self, bar): -+ #Comments should have a space after the hash. -+ if bar: -+ bar += 1 -+ bar = bar * bar -+ return bar -+ else: -+ some_string = """ - Indentation in multiline strings should not be touched. - Only actual code should be reindented. - """ -- return (sys.path, some_string) -+ return (sys.path, some_string) diff --git a/src/test/pythonFiles/formatting/yapfFormatted.py b/src/test/pythonFiles/formatting/yapfFormatted.py deleted file mode 100644 index aa3b079379a2..000000000000 --- a/src/test/pythonFiles/formatting/yapfFormatted.py +++ /dev/null @@ -1,40 +0,0 @@ -import math, sys - - -def example1(): - ####This is a long comment. This should be wrapped to fit within 72 characters. - some_tuple = (1, 2, 3, 'a') - some_variable = { - 'long': - 'Long code lines should be wrapped within 79 characters.', - 'other': [ - math.pi, 100, 200, 300, 9876543210, - 'This is a long string that goes on' - ], - 'more': { - 'inner': 'This whole logical line should be wrapped.', - some_tuple: [1, 20, 300, 40000, 500000000, 60000000000000000] - } - } - return (some_tuple, some_variable) - - -def example2(): - return { - 'has_key() is deprecated': True - }.has_key({'f': 2}.has_key('')) - - -class Example3(object): - def __init__(self, bar): - #Comments should have a space after the hash. - if bar: - bar += 1 - bar = bar * bar - return bar - else: - some_string = """ - Indentation in multiline strings should not be touched. -Only actual code should be reindented. -""" - return (sys.path, some_string) diff --git a/src/test/pythonFiles/linting/cwd/.pylintrc b/src/test/pythonFiles/linting/cwd/.pylintrc deleted file mode 100644 index 8530187c095f..000000000000 --- a/src/test/pythonFiles/linting/cwd/.pylintrc +++ /dev/null @@ -1,2 +0,0 @@ -[MESSAGES CONTROL] -disable=C0326,I0011,I0012,C0304,C0103,W0613,E0001,E1101 diff --git a/src/test/pythonFiles/linting/file.py b/src/test/pythonFiles/linting/file.py deleted file mode 100644 index 7b625a769243..000000000000 --- a/src/test/pythonFiles/linting/file.py +++ /dev/null @@ -1,87 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """this issues a message""" - print (self) - - def meth2(self, arg): - """and this one not""" - # pylint: disable=unused-argument - print (self\ - + "foo") - - def meth3(self): - """test one line disabling""" - # no error - print (self.bla) # pylint: disable=no-member - # error - print (self.blop) - - def meth4(self): - """test re-enabling""" - # pylint: disable=no-member - # no error - print (self.bla) - print (self.blop) - # pylint: enable=no-member - # error - print (self.blip) - - def meth5(self): - """test IF sub-block re-enabling""" - # pylint: disable=no-member - # no error - print (self.bla) - if self.blop: - # pylint: enable=no-member - # error - print (self.blip) - else: - # no error - print (self.blip) - # no error - print (self.blip) - - def meth6(self): - """test TRY/EXCEPT sub-block re-enabling""" - # pylint: disable=no-member - # no error - print (self.bla) - try: - # pylint: enable=no-member - # error - print (self.blip) - except UndefinedName: # pylint: disable=undefined-variable - # no error - print (self.blip) - # no error - print (self.blip) - - def meth7(self): - """test one line block opening disabling""" - if self.blop: # pylint: disable=no-member - # error - print (self.blip) - else: - # error - print (self.blip) - # error - print (self.blip) - - - def meth8(self): - """test late disabling""" - # error - print (self.blip) - # pylint: disable=no-member - # no error - print (self.bla) - print (self.blop) diff --git a/src/test/pythonFiles/linting/flake8config/.flake8 b/src/test/pythonFiles/linting/flake8config/.flake8 deleted file mode 100644 index 99ff2b9f819c..000000000000 --- a/src/test/pythonFiles/linting/flake8config/.flake8 +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -ignore = E302,E901,E127,E261,E261,E261,E303 \ No newline at end of file diff --git a/src/test/pythonFiles/linting/flake8config/file.py b/src/test/pythonFiles/linting/flake8config/file.py deleted file mode 100644 index 9abe4993dddd..000000000000 --- a/src/test/pythonFiles/linting/flake8config/file.py +++ /dev/null @@ -1,86 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """this issues a message""" - print(self) - - def meth2(self, arg): - """and this one not""" - # pylint: disable=unused-argument - print(self + "foo") - - def meth3(self): - """test one line disabling""" - # no error - print(self.bla) # pylint: disable=no-member - # error - print(self.blop) - - def meth4(self): - """test re-enabling""" - # pylint: disable=no-member - # no error - print(self.bla) - print(self.blop) - # pylint: enable=no-member - # error - print(self.blip) - - def meth5(self): - """test IF sub-block re-enabling""" - # pylint: disable=no-member - # no error - print(self.bla) - if self.blop: - # pylint: enable=no-member - # error - print(self.blip) - else: - # no error - print(self.blip) - # no error - print(self.blip) - - def meth6(self): - """test TRY/EXCEPT sub-block re-enabling""" - # pylint: disable=no-member - # no error - print(self.bla) - try: - # pylint: enable=no-member - # error - print(self.blip) - except UndefinedName: # pylint: disable=undefined-variable - # no error - print(self.blip) - # no error - print(self.blip) - - def meth7(self): - """test one line block opening disabling""" - if self.blop: # pylint: disable=no-member - # error - print(self.blip) - else: - # error - print(self.blip) - # error - print(self.blip) - - def meth8(self): - """test late disabling""" - # error - print(self.blip) - # pylint: disable=no-member - # no error - print(self.bla) - print(self.blop) diff --git a/src/test/pythonFiles/linting/minCheck.py b/src/test/pythonFiles/linting/minCheck.py deleted file mode 100644 index d93fa56f7e8a..000000000000 --- a/src/test/pythonFiles/linting/minCheck.py +++ /dev/null @@ -1 +0,0 @@ -filter(lambda x: x == 1, [1, 1, 2]) diff --git a/src/test/pythonFiles/linting/print.py b/src/test/pythonFiles/linting/print.py deleted file mode 100644 index fca61311fc84..000000000000 --- a/src/test/pythonFiles/linting/print.py +++ /dev/null @@ -1 +0,0 @@ -print x \ No newline at end of file diff --git a/src/test/pythonFiles/linting/pycodestyleconfig/.pycodestyle b/src/test/pythonFiles/linting/pycodestyleconfig/.pycodestyle deleted file mode 100644 index b7c78f49db84..000000000000 --- a/src/test/pythonFiles/linting/pycodestyleconfig/.pycodestyle +++ /dev/null @@ -1,2 +0,0 @@ -[pycodestyle] -ignore = E302,E901,E127,E261,E261,E261,E303 diff --git a/src/test/pythonFiles/linting/pycodestyleconfig/file.py b/src/test/pythonFiles/linting/pycodestyleconfig/file.py deleted file mode 100644 index 047ba0dc679e..000000000000 --- a/src/test/pythonFiles/linting/pycodestyleconfig/file.py +++ /dev/null @@ -1,87 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """this issues a message""" - print self - - def meth2(self, arg): - """and this one not""" - # pylint: disable=unused-argument - print self\ - + "foo" - - def meth3(self): - """test one line disabling""" - # no error - print self.bla # pylint: disable=no-member - # error - print self.blop - - def meth4(self): - """test re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - print self.blop - # pylint: enable=no-member - # error - print self.blip - - def meth5(self): - """test IF sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - if self.blop: - # pylint: enable=no-member - # error - print self.blip - else: - # no error - print self.blip - # no error - print self.blip - - def meth6(self): - """test TRY/EXCEPT sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - try: - # pylint: enable=no-member - # error - print self.blip - except UndefinedName: # pylint: disable=undefined-variable - # no error - print self.blip - # no error - print self.blip - - def meth7(self): - """test one line block opening disabling""" - if self.blop: # pylint: disable=no-member - # error - print self.blip - else: - # error - print self.blip - # error - print self.blip - - - def meth8(self): - """test late disabling""" - # error - print self.blip - # pylint: disable=no-member - # no error - print self.bla - print self.blop \ No newline at end of file diff --git a/src/test/pythonFiles/linting/pydocstyleconfig27/.pydocstyle b/src/test/pythonFiles/linting/pydocstyleconfig27/.pydocstyle deleted file mode 100644 index 19020834ad32..000000000000 --- a/src/test/pythonFiles/linting/pydocstyleconfig27/.pydocstyle +++ /dev/null @@ -1,2 +0,0 @@ -[pydocstyle] -ignore=D400,D401,D402,D403,D404,D203,D102,D107 diff --git a/src/test/pythonFiles/linting/pydocstyleconfig27/file.py b/src/test/pythonFiles/linting/pydocstyleconfig27/file.py deleted file mode 100644 index 047ba0dc679e..000000000000 --- a/src/test/pythonFiles/linting/pydocstyleconfig27/file.py +++ /dev/null @@ -1,87 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """this issues a message""" - print self - - def meth2(self, arg): - """and this one not""" - # pylint: disable=unused-argument - print self\ - + "foo" - - def meth3(self): - """test one line disabling""" - # no error - print self.bla # pylint: disable=no-member - # error - print self.blop - - def meth4(self): - """test re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - print self.blop - # pylint: enable=no-member - # error - print self.blip - - def meth5(self): - """test IF sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - if self.blop: - # pylint: enable=no-member - # error - print self.blip - else: - # no error - print self.blip - # no error - print self.blip - - def meth6(self): - """test TRY/EXCEPT sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - try: - # pylint: enable=no-member - # error - print self.blip - except UndefinedName: # pylint: disable=undefined-variable - # no error - print self.blip - # no error - print self.blip - - def meth7(self): - """test one line block opening disabling""" - if self.blop: # pylint: disable=no-member - # error - print self.blip - else: - # error - print self.blip - # error - print self.blip - - - def meth8(self): - """test late disabling""" - # error - print self.blip - # pylint: disable=no-member - # no error - print self.bla - print self.blop \ No newline at end of file diff --git a/src/test/pythonFiles/linting/pylintconfig/.pylintrc b/src/test/pythonFiles/linting/pylintconfig/.pylintrc deleted file mode 100644 index 74ea19101f81..000000000000 --- a/src/test/pythonFiles/linting/pylintconfig/.pylintrc +++ /dev/null @@ -1,2 +0,0 @@ -[MESSAGES CONTROL] -disable=I0011,C0304,C0103,W0613,E0001,E1101 diff --git a/src/test/pythonFiles/linting/pylintconfig/file.py b/src/test/pythonFiles/linting/pylintconfig/file.py deleted file mode 100644 index 047ba0dc679e..000000000000 --- a/src/test/pythonFiles/linting/pylintconfig/file.py +++ /dev/null @@ -1,87 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """this issues a message""" - print self - - def meth2(self, arg): - """and this one not""" - # pylint: disable=unused-argument - print self\ - + "foo" - - def meth3(self): - """test one line disabling""" - # no error - print self.bla # pylint: disable=no-member - # error - print self.blop - - def meth4(self): - """test re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - print self.blop - # pylint: enable=no-member - # error - print self.blip - - def meth5(self): - """test IF sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - if self.blop: - # pylint: enable=no-member - # error - print self.blip - else: - # no error - print self.blip - # no error - print self.blip - - def meth6(self): - """test TRY/EXCEPT sub-block re-enabling""" - # pylint: disable=no-member - # no error - print self.bla - try: - # pylint: enable=no-member - # error - print self.blip - except UndefinedName: # pylint: disable=undefined-variable - # no error - print self.blip - # no error - print self.blip - - def meth7(self): - """test one line block opening disabling""" - if self.blop: # pylint: disable=no-member - # error - print self.blip - else: - # error - print self.blip - # error - print self.blip - - - def meth8(self): - """test late disabling""" - # error - print self.blip - # pylint: disable=no-member - # no error - print self.bla - print self.blop \ No newline at end of file diff --git a/src/test/pythonFiles/linting/pylintconfig/file2.py b/src/test/pythonFiles/linting/pylintconfig/file2.py deleted file mode 100644 index f375c984aa2e..000000000000 --- a/src/test/pythonFiles/linting/pylintconfig/file2.py +++ /dev/null @@ -1,19 +0,0 @@ -"""pylint option block-disable""" - -__revision__ = None - -class Foo(object): - """block-disable test""" - - def __init__(self): - pass - - def meth1(self, arg): - """meth1""" - print self.blop - - def meth2(self, arg): - """meth2""" - # pylint: disable=unused-argument - print self\ - + "foo" diff --git a/src/test/pythonFiles/linting/threeLineLints.py b/src/test/pythonFiles/linting/threeLineLints.py deleted file mode 100644 index e8b578d93f11..000000000000 --- a/src/test/pythonFiles/linting/threeLineLints.py +++ /dev/null @@ -1,24 +0,0 @@ -"""pylint messages with three lines of output""" - -__revision__ = None - -class Foo(object): - - def __init__(self): - pass - - def meth1(self,arg): - """missing a space between 'self' and 'arg'. This should trigger the - following three line lint warning:: - - C: 10, 0: Exactly one space required after comma - def meth1(self,arg): - ^ (bad-whitespace) - - The following three lines of tuples should also cause three-line lint - errors due to "Exactly one space required after comma" messages. - """ - a = (1,2) - b = (1,2) - c = (1,2) - print (self) diff --git a/src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/config.py b/src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/config.py deleted file mode 100644 index dee2d1ae9a6b..000000000000 --- a/src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/config.py +++ /dev/null @@ -1,114 +0,0 @@ -# The default ``config.py`` -# flake8: noqa - - -def set_prefs(prefs): - """This function is called before opening the project""" - - # Specify which files and folders to ignore in the project. - # Changes to ignored resources are not added to the history and - # VCSs. Also they are not returned in `Project.get_files()`. - # Note that ``?`` and ``*`` match all characters but slashes. - # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' - # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' - # '.svn': matches 'pkg/.svn' and all of its children - # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' - # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' - prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject', - '.hg', '.svn', '_svn', '.git', '.tox'] - - # Specifies which files should be considered python files. It is - # useful when you have scripts inside your project. Only files - # ending with ``.py`` are considered to be python files by - # default. - # prefs['python_files'] = ['*.py'] - - # Custom source folders: By default rope searches the project - # for finding source folders (folders that should be searched - # for finding modules). You can add paths to that list. Note - # that rope guesses project source folders correctly most of the - # time; use this if you have any problems. - # The folders should be relative to project root and use '/' for - # separating folders regardless of the platform rope is running on. - # 'src/my_source_folder' for instance. - # prefs.add('source_folders', 'src') - - # You can extend python path for looking up modules - # prefs.add('python_path', '~/python/') - - # Should rope save object information or not. - prefs['save_objectdb'] = True - prefs['compress_objectdb'] = False - - # If `True`, rope analyzes each module when it is being saved. - prefs['automatic_soa'] = True - # The depth of calls to follow in static object analysis - prefs['soa_followed_calls'] = 0 - - # If `False` when running modules or unit tests "dynamic object - # analysis" is turned off. This makes them much faster. - prefs['perform_doa'] = True - - # Rope can check the validity of its object DB when running. - prefs['validate_objectdb'] = True - - # How many undos to hold? - prefs['max_history_items'] = 32 - - # Shows whether to save history across sessions. - prefs['save_history'] = True - prefs['compress_history'] = False - - # Set the number spaces used for indenting. According to - # :PEP:`8`, it is best to use 4 spaces. Since most of rope's - # unit-tests use 4 spaces it is more reliable, too. - prefs['indent_size'] = 4 - - # Builtin and c-extension modules that are allowed to be imported - # and inspected by rope. - prefs['extension_modules'] = [] - - # Add all standard c-extensions to extension_modules list. - prefs['import_dynload_stdmods'] = True - - # If `True` modules with syntax errors are considered to be empty. - # The default value is `False`; When `False` syntax errors raise - # `rope.base.exceptions.ModuleSyntaxError` exception. - prefs['ignore_syntax_errors'] = False - - # If `True`, rope ignores unresolvable imports. Otherwise, they - # appear in the importing namespace. - prefs['ignore_bad_imports'] = False - - # If `True`, rope will insert new module imports as - # `from import ` by default. - prefs['prefer_module_from_imports'] = False - - # If `True`, rope will transform a comma list of imports into - # multiple separate import statements when organizing - # imports. - prefs['split_imports'] = False - - # If `True`, rope will remove all top-level import statements and - # reinsert them at the top of the module when making changes. - prefs['pull_imports_to_top'] = True - - # If `True`, rope will sort imports alphabetically by module name instead - # of alphabetically by import statement, with from imports after normal - # imports. - prefs['sort_imports_alphabetically'] = False - - # Location of implementation of - # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general - # case, you don't have to change this value, unless you're an rope expert. - # Change this value to inject you own implementations of interfaces - # listed in module rope.base.oi.type_hinting.providers.interfaces - # For example, you can add you own providers for Django Models, or disable - # the search type-hinting in a class hierarchy, etc. - prefs['type_hinting_factory'] = ( - 'rope.base.oi.type_hinting.factory.default_type_hinting_factory') - - -def project_opened(project): - """This function is called after opening the project""" - # Do whatever you like here! diff --git a/src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/objectdb b/src/test/pythonFiles/refactoring/source folder/.vscode/.ropeproject/objectdb deleted file mode 100644 index 0a47446c0ad231c193bdd44ff327ba2ab28bf3d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6 NcmZo*sx4&D0{{kv0iOT> diff --git a/src/test/pythonFiles/sorting/noconfig/after.py b/src/test/pythonFiles/sorting/noconfig/after.py deleted file mode 100644 index b768c396014c..000000000000 --- a/src/test/pythonFiles/sorting/noconfig/after.py +++ /dev/null @@ -1,16 +0,0 @@ -import io; sys; json -import traceback - -import rope -import rope.base.project -import rope.base.taskhandle -from rope.base import libutils -from rope.refactor.extract import ExtractMethod, ExtractVariable -from rope.refactor.rename import Rename - -WORKSPACE_ROOT = sys.argv[1] -ROPE_PROJECT_FOLDER = sys.argv[2] - - -def test(): - pass diff --git a/src/test/pythonFiles/sorting/noconfig/before.py b/src/test/pythonFiles/sorting/noconfig/before.py deleted file mode 100644 index fcd7318b5c02..000000000000 --- a/src/test/pythonFiles/sorting/noconfig/before.py +++ /dev/null @@ -1,18 +0,0 @@ -import io; sys; json -import traceback -import rope - -import rope.base.project -import rope.base.taskhandle - -WORKSPACE_ROOT = sys.argv[1] -ROPE_PROJECT_FOLDER = sys.argv[2] - - -def test(): - pass - -from rope.base import libutils -from rope.refactor.rename import Rename -from rope.refactor.extract import ExtractMethod, ExtractVariable - \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/noconfig/original.py b/src/test/pythonFiles/sorting/noconfig/original.py deleted file mode 100644 index fcd7318b5c02..000000000000 --- a/src/test/pythonFiles/sorting/noconfig/original.py +++ /dev/null @@ -1,18 +0,0 @@ -import io; sys; json -import traceback -import rope - -import rope.base.project -import rope.base.taskhandle - -WORKSPACE_ROOT = sys.argv[1] -ROPE_PROJECT_FOLDER = sys.argv[2] - - -def test(): - pass - -from rope.base import libutils -from rope.refactor.rename import Rename -from rope.refactor.extract import ExtractMethod, ExtractVariable - \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/.isort.cfg b/src/test/pythonFiles/sorting/withconfig/.isort.cfg deleted file mode 100644 index 68da732e2b4b..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/.isort.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[settings] -force_single_line=True \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/after.py b/src/test/pythonFiles/sorting/withconfig/after.py deleted file mode 100644 index e1fd315dbf92..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/after.py +++ /dev/null @@ -1,3 +0,0 @@ -from third_party import (lib1, lib2, lib3, - lib4, lib5, lib6, - lib7, lib8, lib9) \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/before.1.py b/src/test/pythonFiles/sorting/withconfig/before.1.py deleted file mode 100644 index e1fd315dbf92..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/before.1.py +++ /dev/null @@ -1,3 +0,0 @@ -from third_party import (lib1, lib2, lib3, - lib4, lib5, lib6, - lib7, lib8, lib9) \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/before.py b/src/test/pythonFiles/sorting/withconfig/before.py deleted file mode 100644 index e1fd315dbf92..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/before.py +++ /dev/null @@ -1,3 +0,0 @@ -from third_party import (lib1, lib2, lib3, - lib4, lib5, lib6, - lib7, lib8, lib9) \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/original.1.py b/src/test/pythonFiles/sorting/withconfig/original.1.py deleted file mode 100644 index e1fd315dbf92..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/original.1.py +++ /dev/null @@ -1,3 +0,0 @@ -from third_party import (lib1, lib2, lib3, - lib4, lib5, lib6, - lib7, lib8, lib9) \ No newline at end of file diff --git a/src/test/pythonFiles/sorting/withconfig/original.py b/src/test/pythonFiles/sorting/withconfig/original.py deleted file mode 100644 index e1fd315dbf92..000000000000 --- a/src/test/pythonFiles/sorting/withconfig/original.py +++ /dev/null @@ -1,3 +0,0 @@ -from third_party import (lib1, lib2, lib3, - lib4, lib5, lib6, - lib7, lib8, lib9) \ No newline at end of file diff --git a/src/test/pythonFiles/datascience/simple_nb.ipynb b/src/test/python_files/datascience/simple_nb.ipynb similarity index 100% rename from src/test/pythonFiles/datascience/simple_nb.ipynb rename to src/test/python_files/datascience/simple_nb.ipynb diff --git a/src/test/pythonFiles/datascience/simple_note_book.py b/src/test/python_files/datascience/simple_note_book.py similarity index 100% rename from src/test/pythonFiles/datascience/simple_note_book.py rename to src/test/python_files/datascience/simple_note_book.py diff --git a/src/test/pythonFiles/debugging/forever.py b/src/test/python_files/debugging/forever.py similarity index 100% rename from src/test/pythonFiles/debugging/forever.py rename to src/test/python_files/debugging/forever.py diff --git a/src/test/pythonFiles/debugging/logMessage.py b/src/test/python_files/debugging/logMessage.py similarity index 100% rename from src/test/pythonFiles/debugging/logMessage.py rename to src/test/python_files/debugging/logMessage.py diff --git a/src/test/pythonFiles/debugging/loopyTest.py b/src/test/python_files/debugging/loopyTest.py similarity index 100% rename from src/test/pythonFiles/debugging/loopyTest.py rename to src/test/python_files/debugging/loopyTest.py diff --git a/src/test/pythonFiles/debugging/multiThread.py b/src/test/python_files/debugging/multiThread.py similarity index 100% rename from src/test/pythonFiles/debugging/multiThread.py rename to src/test/python_files/debugging/multiThread.py diff --git a/src/test/pythonFiles/debugging/printSysArgv.py b/src/test/python_files/debugging/printSysArgv.py similarity index 100% rename from src/test/pythonFiles/debugging/printSysArgv.py rename to src/test/python_files/debugging/printSysArgv.py diff --git a/src/test/pythonFiles/debugging/sample2.py b/src/test/python_files/debugging/sample2.py similarity index 100% rename from src/test/pythonFiles/debugging/sample2.py rename to src/test/python_files/debugging/sample2.py diff --git a/src/test/pythonFiles/debugging/sample2WithoutSleep.py b/src/test/python_files/debugging/sample2WithoutSleep.py similarity index 100% rename from src/test/pythonFiles/debugging/sample2WithoutSleep.py rename to src/test/python_files/debugging/sample2WithoutSleep.py diff --git a/src/test/pythonFiles/debugging/sample3WithEx.py b/src/test/python_files/debugging/sample3WithEx.py similarity index 100% rename from src/test/pythonFiles/debugging/sample3WithEx.py rename to src/test/python_files/debugging/sample3WithEx.py diff --git a/src/test/pythonFiles/debugging/sampleWithAssertEx.py b/src/test/python_files/debugging/sampleWithAssertEx.py similarity index 100% rename from src/test/pythonFiles/debugging/sampleWithAssertEx.py rename to src/test/python_files/debugging/sampleWithAssertEx.py diff --git a/src/test/pythonFiles/debugging/sampleWithSleep.py b/src/test/python_files/debugging/sampleWithSleep.py similarity index 100% rename from src/test/pythonFiles/debugging/sampleWithSleep.py rename to src/test/python_files/debugging/sampleWithSleep.py diff --git a/src/test/pythonFiles/debugging/simplePrint.py b/src/test/python_files/debugging/simplePrint.py similarity index 100% rename from src/test/pythonFiles/debugging/simplePrint.py rename to src/test/python_files/debugging/simplePrint.py diff --git a/src/test/pythonFiles/debugging/stackFrame.py b/src/test/python_files/debugging/stackFrame.py similarity index 100% rename from src/test/pythonFiles/debugging/stackFrame.py rename to src/test/python_files/debugging/stackFrame.py diff --git a/src/test/pythonFiles/debugging/startAndWait.py b/src/test/python_files/debugging/startAndWait.py similarity index 100% rename from src/test/pythonFiles/debugging/startAndWait.py rename to src/test/python_files/debugging/startAndWait.py diff --git a/src/test/pythonFiles/debugging/stdErrOutput.py b/src/test/python_files/debugging/stdErrOutput.py similarity index 100% rename from src/test/pythonFiles/debugging/stdErrOutput.py rename to src/test/python_files/debugging/stdErrOutput.py diff --git a/src/test/pythonFiles/debugging/stdOutOutput.py b/src/test/python_files/debugging/stdOutOutput.py similarity index 100% rename from src/test/pythonFiles/debugging/stdOutOutput.py rename to src/test/python_files/debugging/stdOutOutput.py diff --git a/src/test/pythonFiles/debugging/wait_for_file.py b/src/test/python_files/debugging/wait_for_file.py similarity index 100% rename from src/test/pythonFiles/debugging/wait_for_file.py rename to src/test/python_files/debugging/wait_for_file.py diff --git a/src/test/pythonFiles/dummy.py b/src/test/python_files/dummy.py similarity index 100% rename from src/test/pythonFiles/dummy.py rename to src/test/python_files/dummy.py diff --git a/src/test/pythonFiles/environments/conda/Scripts/conda.exe b/src/test/python_files/environments/conda/Scripts/conda.exe similarity index 100% rename from src/test/pythonFiles/environments/conda/Scripts/conda.exe rename to src/test/python_files/environments/conda/Scripts/conda.exe diff --git a/src/test/pythonFiles/environments/conda/bin/python b/src/test/python_files/environments/conda/bin/python similarity index 100% rename from src/test/pythonFiles/environments/conda/bin/python rename to src/test/python_files/environments/conda/bin/python diff --git a/src/test/pythonFiles/environments/conda/envs/numpy/bin/python b/src/test/python_files/environments/conda/envs/numpy/bin/python similarity index 100% rename from src/test/pythonFiles/environments/conda/envs/numpy/bin/python rename to src/test/python_files/environments/conda/envs/numpy/bin/python diff --git a/src/test/pythonFiles/environments/conda/envs/numpy/python.exe b/src/test/python_files/environments/conda/envs/numpy/python.exe similarity index 100% rename from src/test/pythonFiles/environments/conda/envs/numpy/python.exe rename to src/test/python_files/environments/conda/envs/numpy/python.exe diff --git a/src/test/pythonFiles/environments/conda/envs/scipy/bin/python b/src/test/python_files/environments/conda/envs/scipy/bin/python similarity index 100% rename from src/test/pythonFiles/environments/conda/envs/scipy/bin/python rename to src/test/python_files/environments/conda/envs/scipy/bin/python diff --git a/src/test/pythonFiles/environments/conda/envs/scipy/python.exe b/src/test/python_files/environments/conda/envs/scipy/python.exe similarity index 100% rename from src/test/pythonFiles/environments/conda/envs/scipy/python.exe rename to src/test/python_files/environments/conda/envs/scipy/python.exe diff --git a/src/test/pythonFiles/environments/path1/one b/src/test/python_files/environments/path1/one similarity index 100% rename from src/test/pythonFiles/environments/path1/one rename to src/test/python_files/environments/path1/one diff --git a/src/test/pythonFiles/environments/path1/one.exe b/src/test/python_files/environments/path1/one.exe similarity index 100% rename from src/test/pythonFiles/environments/path1/one.exe rename to src/test/python_files/environments/path1/one.exe diff --git a/src/test/pythonFiles/environments/path1/python.exe b/src/test/python_files/environments/path1/python.exe similarity index 100% rename from src/test/pythonFiles/environments/path1/python.exe rename to src/test/python_files/environments/path1/python.exe diff --git a/src/test/pythonFiles/environments/path2/one b/src/test/python_files/environments/path2/one similarity index 100% rename from src/test/pythonFiles/environments/path2/one rename to src/test/python_files/environments/path2/one diff --git a/src/test/pythonFiles/environments/path2/one.exe b/src/test/python_files/environments/path2/one.exe similarity index 100% rename from src/test/pythonFiles/environments/path2/one.exe rename to src/test/python_files/environments/path2/one.exe diff --git a/src/test/pythonFiles/environments/path2/python.exe b/src/test/python_files/environments/path2/python.exe similarity index 100% rename from src/test/pythonFiles/environments/path2/python.exe rename to src/test/python_files/environments/path2/python.exe diff --git a/src/test/pythonFiles/intellisense/test.py b/src/test/python_files/intellisense/test.py similarity index 100% rename from src/test/pythonFiles/intellisense/test.py rename to src/test/python_files/intellisense/test.py diff --git a/src/test/pythonFiles/shebang/plain.py b/src/test/python_files/shebang/plain.py similarity index 100% rename from src/test/pythonFiles/shebang/plain.py rename to src/test/python_files/shebang/plain.py diff --git a/src/test/pythonFiles/shebang/shebang.py b/src/test/python_files/shebang/shebang.py similarity index 100% rename from src/test/pythonFiles/shebang/shebang.py rename to src/test/python_files/shebang/shebang.py diff --git a/src/test/pythonFiles/shebang/shebangEnv.py b/src/test/python_files/shebang/shebangEnv.py similarity index 100% rename from src/test/pythonFiles/shebang/shebangEnv.py rename to src/test/python_files/shebang/shebangEnv.py diff --git a/src/test/pythonFiles/shebang/shebangInvalid.py b/src/test/python_files/shebang/shebangInvalid.py similarity index 100% rename from src/test/pythonFiles/shebang/shebangInvalid.py rename to src/test/python_files/shebang/shebangInvalid.py diff --git a/src/test/pythonFiles/tensorBoard/noMatch.py b/src/test/python_files/tensorBoard/noMatch.py similarity index 100% rename from src/test/pythonFiles/tensorBoard/noMatch.py rename to src/test/python_files/tensorBoard/noMatch.py diff --git a/src/test/pythonFiles/tensorBoard/sourcefile.py b/src/test/python_files/tensorBoard/sourcefile.py similarity index 100% rename from src/test/pythonFiles/tensorBoard/sourcefile.py rename to src/test/python_files/tensorBoard/sourcefile.py diff --git a/src/test/pythonFiles/tensorBoard/tensorboard_import.ipynb b/src/test/python_files/tensorBoard/tensorboard_import.ipynb similarity index 100% rename from src/test/pythonFiles/tensorBoard/tensorboard_import.ipynb rename to src/test/python_files/tensorBoard/tensorboard_import.ipynb diff --git a/src/test/pythonFiles/tensorBoard/tensorboard_imports.py b/src/test/python_files/tensorBoard/tensorboard_imports.py similarity index 100% rename from src/test/pythonFiles/tensorBoard/tensorboard_imports.py rename to src/test/python_files/tensorBoard/tensorboard_imports.py diff --git a/src/test/pythonFiles/tensorBoard/tensorboard_launch.py b/src/test/python_files/tensorBoard/tensorboard_launch.py similarity index 100% rename from src/test/pythonFiles/tensorBoard/tensorboard_launch.py rename to src/test/python_files/tensorBoard/tensorboard_launch.py diff --git a/src/test/pythonFiles/tensorBoard/tensorboard_nbextension.ipynb b/src/test/python_files/tensorBoard/tensorboard_nbextension.ipynb similarity index 100% rename from src/test/pythonFiles/tensorBoard/tensorboard_nbextension.ipynb rename to src/test/python_files/tensorBoard/tensorboard_nbextension.ipynb diff --git a/src/test/pythonFiles/terminalExec/sample1_normalized.py b/src/test/python_files/terminalExec/sample1_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample1_normalized.py rename to src/test/python_files/terminalExec/sample1_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample1_normalized_selection.py b/src/test/python_files/terminalExec/sample1_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample1_normalized_selection.py rename to src/test/python_files/terminalExec/sample1_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample1_raw.py b/src/test/python_files/terminalExec/sample1_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample1_raw.py rename to src/test/python_files/terminalExec/sample1_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample2_normalized.py b/src/test/python_files/terminalExec/sample2_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample2_normalized.py rename to src/test/python_files/terminalExec/sample2_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample2_normalized_selection.py b/src/test/python_files/terminalExec/sample2_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample2_normalized_selection.py rename to src/test/python_files/terminalExec/sample2_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample2_raw.py b/src/test/python_files/terminalExec/sample2_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample2_raw.py rename to src/test/python_files/terminalExec/sample2_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample3_normalized.py b/src/test/python_files/terminalExec/sample3_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample3_normalized.py rename to src/test/python_files/terminalExec/sample3_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample3_normalized_selection.py b/src/test/python_files/terminalExec/sample3_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample3_normalized_selection.py rename to src/test/python_files/terminalExec/sample3_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample3_raw.py b/src/test/python_files/terminalExec/sample3_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample3_raw.py rename to src/test/python_files/terminalExec/sample3_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample4_normalized.py b/src/test/python_files/terminalExec/sample4_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample4_normalized.py rename to src/test/python_files/terminalExec/sample4_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample4_normalized_selection.py b/src/test/python_files/terminalExec/sample4_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample4_normalized_selection.py rename to src/test/python_files/terminalExec/sample4_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample4_raw.py b/src/test/python_files/terminalExec/sample4_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample4_raw.py rename to src/test/python_files/terminalExec/sample4_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample5_normalized.py b/src/test/python_files/terminalExec/sample5_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample5_normalized.py rename to src/test/python_files/terminalExec/sample5_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample5_normalized_selection.py b/src/test/python_files/terminalExec/sample5_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample5_normalized_selection.py rename to src/test/python_files/terminalExec/sample5_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample5_raw.py b/src/test/python_files/terminalExec/sample5_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample5_raw.py rename to src/test/python_files/terminalExec/sample5_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample6_normalized.py b/src/test/python_files/terminalExec/sample6_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample6_normalized.py rename to src/test/python_files/terminalExec/sample6_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample6_normalized_selection.py b/src/test/python_files/terminalExec/sample6_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample6_normalized_selection.py rename to src/test/python_files/terminalExec/sample6_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample6_raw.py b/src/test/python_files/terminalExec/sample6_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample6_raw.py rename to src/test/python_files/terminalExec/sample6_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample7_normalized.py b/src/test/python_files/terminalExec/sample7_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample7_normalized.py rename to src/test/python_files/terminalExec/sample7_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample7_normalized_selection.py b/src/test/python_files/terminalExec/sample7_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample7_normalized_selection.py rename to src/test/python_files/terminalExec/sample7_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample7_raw.py b/src/test/python_files/terminalExec/sample7_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample7_raw.py rename to src/test/python_files/terminalExec/sample7_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample8_normalized.py b/src/test/python_files/terminalExec/sample8_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample8_normalized.py rename to src/test/python_files/terminalExec/sample8_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample8_normalized_selection.py b/src/test/python_files/terminalExec/sample8_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample8_normalized_selection.py rename to src/test/python_files/terminalExec/sample8_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample8_raw.py b/src/test/python_files/terminalExec/sample8_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample8_raw.py rename to src/test/python_files/terminalExec/sample8_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample_invalid_smart_selection.py b/src/test/python_files/terminalExec/sample_invalid_smart_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample_invalid_smart_selection.py rename to src/test/python_files/terminalExec/sample_invalid_smart_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample_normalized.py b/src/test/python_files/terminalExec/sample_normalized.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample_normalized.py rename to src/test/python_files/terminalExec/sample_normalized.py diff --git a/src/test/pythonFiles/terminalExec/sample_normalized_selection.py b/src/test/python_files/terminalExec/sample_normalized_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample_normalized_selection.py rename to src/test/python_files/terminalExec/sample_normalized_selection.py diff --git a/src/test/pythonFiles/terminalExec/sample_raw.py b/src/test/python_files/terminalExec/sample_raw.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample_raw.py rename to src/test/python_files/terminalExec/sample_raw.py diff --git a/src/test/pythonFiles/terminalExec/sample_smart_selection.py b/src/test/python_files/terminalExec/sample_smart_selection.py similarity index 100% rename from src/test/pythonFiles/terminalExec/sample_smart_selection.py rename to src/test/python_files/terminalExec/sample_smart_selection.py diff --git a/src/test/smoke/datascience.smoke.test.ts b/src/test/smoke/datascience.smoke.test.ts index ebd101b5849f..30298183c511 100644 --- a/src/test/smoke/datascience.smoke.test.ts +++ b/src/test/smoke/datascience.smoke.test.ts @@ -35,7 +35,7 @@ suite('Smoke Test: Datascience', () => { EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', - 'pythonFiles', + 'python_files', 'datascience', 'simple_note_book.py', ); @@ -60,7 +60,7 @@ suite('Smoke Test: Datascience', () => { EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', - 'pythonFiles', + 'python_files', 'datascience', 'simple_nb.ipynb', ); diff --git a/src/test/smoke/jedilsp.smoke.test.ts b/src/test/smoke/jedilsp.smoke.test.ts index acde436442e1..e25d2bcdd1cc 100644 --- a/src/test/smoke/jedilsp.smoke.test.ts +++ b/src/test/smoke/jedilsp.smoke.test.ts @@ -24,7 +24,7 @@ suite('Smoke Test: Jedi LSP', () => { teardown(closeActiveWindows); test('Verify diagnostics on a python file', async () => { - const file = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'pythonFiles', 'intellisense', 'test.py'); + const file = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'python_files', 'intellisense', 'test.py'); const outputFile = path.join(path.dirname(file), 'ds.log'); if (await fs.pathExists(outputFile)) { await fs.unlink(outputFile); diff --git a/src/test/tensorBoard/tensorBoardSession.test.ts b/src/test/tensorBoard/tensorBoardSession.test.ts index 2faaf8e39611..626740f4f530 100644 --- a/src/test/tensorBoard/tensorBoardSession.test.ts +++ b/src/test/tensorBoard/tensorBoardSession.test.ts @@ -461,7 +461,7 @@ suite('TensorBoard session creation', async () => { EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', - 'pythonFiles', + 'python_files', 'tensorBoard', 'sourcefile.py', ); diff --git a/src/test/terminals/codeExecution/helper.test.ts b/src/test/terminals/codeExecution/helper.test.ts index ac47037a2344..2922fe9a7ef4 100644 --- a/src/test/terminals/codeExecution/helper.test.ts +++ b/src/test/terminals/codeExecution/helper.test.ts @@ -32,7 +32,7 @@ import { CodeExecutionHelper } from '../../../client/terminals/codeExecution/hel import { ICodeExecutionHelper } from '../../../client/terminals/types'; import { PYTHON_PATH } from '../../common'; -const TEST_FILES_PATH = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'pythonFiles', 'terminalExec'); +const TEST_FILES_PATH = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'python_files', 'terminalExec'); suite('Terminal - Code Execution Helper', () => { let documentManager: TypeMoq.IMock; diff --git a/src/test/terminals/codeExecution/smartSend.test.ts b/src/test/terminals/codeExecution/smartSend.test.ts index 4963629d0fda..99678ec1cb8d 100644 --- a/src/test/terminals/codeExecution/smartSend.test.ts +++ b/src/test/terminals/codeExecution/smartSend.test.ts @@ -24,7 +24,7 @@ import { Architecture } from '../../../client/common/utils/platform'; import { ProcessService } from '../../../client/common/process/proc'; import { l10n } from '../../mocks/vsc'; -const TEST_FILES_PATH = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'pythonFiles', 'terminalExec'); +const TEST_FILES_PATH = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'python_files', 'terminalExec'); suite('REPL - Smart Send', () => { let documentManager: TypeMoq.IMock; diff --git a/src/test/testing/common/debugLauncher.unit.test.ts b/src/test/testing/common/debugLauncher.unit.test.ts index 1c69bac20593..ef3e678c13f7 100644 --- a/src/test/testing/common/debugLauncher.unit.test.ts +++ b/src/test/testing/common/debugLauncher.unit.test.ts @@ -151,10 +151,10 @@ suite('Unit Tests - Debug Launcher', () => { if (!pythonTestAdapterRewriteExperiment) { switch (testProvider) { case 'unittest': { - return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'visualstudio_py_testlauncher.py'); + return path.join(EXTENSION_ROOT_DIR, 'python_files', 'visualstudio_py_testlauncher.py'); } case 'pytest': { - return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'testlauncher.py'); + return path.join(EXTENSION_ROOT_DIR, 'python_files', 'testlauncher.py'); } default: { throw new Error(`Unknown test provider '${testProvider}'`); @@ -208,14 +208,14 @@ suite('Unit Tests - Debug Launcher', () => { if (!expected) { expected = getDefaultDebugConfig(); } - expected.rules = [{ path: path.join(EXTENSION_ROOT_DIR, 'pythonFiles'), include: false }]; + expected.rules = [{ path: path.join(EXTENSION_ROOT_DIR, 'python_files'), include: false }]; expected.program = testLaunchScript; expected.args = options.args; if (!expected.cwd) { expected.cwd = workspaceFolders[0].uri.fsPath; } - const pluginPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); + const pluginPath = path.join(EXTENSION_ROOT_DIR, 'python_files'); const pythonPath = `${pluginPath}${path.delimiter}${expected.cwd}`; expected.env.PYTHONPATH = pythonPath; @@ -345,7 +345,7 @@ suite('Unit Tests - Debug Launcher', () => { }; const expected = getDefaultDebugConfig(); expected.cwd = 'path/to/settings/cwd'; - const pluginPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); + const pluginPath = path.join(EXTENSION_ROOT_DIR, 'python_files'); const pythonPath = `${pluginPath}${path.delimiter}${expected.cwd}`; expected.env.PYTHONPATH = pythonPath; diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts index 45f6d4ffa8eb..3b1e8fec6d6d 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts @@ -46,7 +46,7 @@ suite('pytest test discovery adapter', () => { uuid = 'uuid123'; expectedPath = path.join('/', 'my', 'test', 'path'); uri = Uri.file(expectedPath); - const relativePathToPytest = 'pythonFiles'; + const relativePathToPytest = 'python_files'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); expectedExtraVariables = { PYTHONPATH: fullPluginPath, diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index d2ab19368b2d..26a19ed0fd09 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -169,7 +169,7 @@ suite('pytest test execution adapter', () => { await deferred4.promise; mockProc.trigger('close'); - const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); + const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'python_files'); const pathToPythonScript = path.join(pathToPythonFiles, 'vscode_pytest', 'run_pytest_script.py'); const rootDirArg = `--rootdir=${myTestPath}`; const expectedArgs = [pathToPythonScript, rootDirArg]; @@ -237,7 +237,7 @@ suite('pytest test execution adapter', () => { await deferred4.promise; mockProc.trigger('close'); - const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); + const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'python_files'); const pathToPythonScript = path.join(pathToPythonFiles, 'vscode_pytest', 'run_pytest_script.py'); const expectedArgs = [pathToPythonScript, `--rootdir=${newCwd}`]; const expectedExtraVariables = { diff --git a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts index 0eee88120f6a..c5fb0d421da6 100644 --- a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts @@ -42,7 +42,7 @@ suite('Unittest test discovery adapter', () => { } as unknown) as ITestServer; const uri = Uri.file('/foo/bar'); - const script = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + const script = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'discovery.py'); const adapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings, outputChannel.object); adapter.discoverTests(uri); @@ -78,7 +78,7 @@ suite('Unittest test discovery adapter', () => { const uri = Uri.file('/foo/bar'); const newCwd = '/foo'; - const script = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + const script = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'discovery.py'); const adapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings, outputChannel.object); adapter.discoverTests(uri); diff --git a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts index e2903d353bbf..bb82f49b4777 100644 --- a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts @@ -50,7 +50,7 @@ suite('Unittest test execution adapter', () => { } as unknown) as ITestServer; const uri = Uri.file('/foo/bar'); - const script = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); + const script = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'execution.py'); const adapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings, outputChannel.object); const testIds = ['test1id', 'test2id']; @@ -94,7 +94,7 @@ suite('Unittest test execution adapter', () => { const newCwd = '/foo'; const uri = Uri.file('/foo/bar'); - const script = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); + const script = path.join(EXTENSION_ROOT_DIR, 'python_files', 'unittestadapter', 'execution.py'); const adapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings, outputChannel.object); const testIds = ['test1id', 'test2id']; From e45fe135644d592031f3b076b519e9ba67439118 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 4 Mar 2024 10:58:10 -0800 Subject: [PATCH 043/767] fixes bug which stops duplicate pytest args (#23024) fixes https://github.com/microsoft/vscode-python/issues/22999#issuecomment-1975942917 --- .../testing/testController/common/utils.ts | 42 ++++++++++++---- .../testing/testController/utils.unit.test.ts | 49 +++++++++++-------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 0e81154a899c..e98fa99b9bd2 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -359,14 +359,25 @@ export function splitTestNameWithRegex(testName: string): [string, string] { * @param args - Readonly array of strings to be converted to a map. * @returns A map representation of the input strings. */ -export const argsToMap = (args: ReadonlyArray): { [key: string]: string | null | undefined } => { - const map: { [key: string]: string | null } = {}; +export const argsToMap = (args: ReadonlyArray): { [key: string]: Array | null | undefined } => { + const map: { [key: string]: Array | null } = {}; for (const arg of args) { const delimiter = arg.indexOf('='); if (delimiter === -1) { + // If no delimiter is found, the entire string becomes a key with a value of null. map[arg] = null; } else { - map[arg.slice(0, delimiter)] = arg.slice(delimiter + 1); + const key = arg.slice(0, delimiter); + const value = arg.slice(delimiter + 1); + if (map[key]) { + // add to the array + const arr = map[key] as string[]; + arr.push(value); + map[key] = arr; + } else { + // create a new array + map[key] = [value]; + } } } @@ -383,7 +394,7 @@ export const argsToMap = (args: ReadonlyArray): { [key: string]: string * @param map - The map to be converted to an array of strings. * @returns An array of strings representation of the input map. */ -export const mapToArgs = (map: { [key: string]: string | null | undefined }): string[] => { +export const mapToArgs = (map: { [key: string]: Array | null | undefined }): string[] => { const out: string[] = []; for (const key of Object.keys(map)) { const value = map[key]; @@ -391,8 +402,14 @@ export const mapToArgs = (map: { [key: string]: string | null | undefined }): st // eslint-disable-next-line no-continue continue; } - - out.push(value === null ? key : `${key}=${value}`); + if (value === null) { + out.push(key); + } else { + const values = Array.isArray(value) ? (value as string[]) : [value]; + for (const v of values) { + out.push(`${key}=${v}`); + } + } } return out; @@ -407,13 +424,18 @@ export const mapToArgs = (map: { [key: string]: string | null | undefined }): st * @returns The updated map. */ export function addArgIfNotExist( - map: { [key: string]: string | null | undefined }, + map: { [key: string]: Array | null | undefined }, argKey: string, argValue: string | null, -): { [key: string]: string | null | undefined } { +): { [key: string]: Array | null | undefined } { // Only add the argument if it doesn't exist in the map. if (map[argKey] === undefined) { - map[argKey] = argValue; + // if null then set to null, otherwise set to an array with the value + if (argValue === null) { + map[argKey] = null; + } else { + map[argKey] = [argValue]; + } } return map; @@ -426,6 +448,6 @@ export function addArgIfNotExist( * @param argKey - The argument key to be checked. * @returns True if the argument key exists in the map, false otherwise. */ -export function argKeyExists(map: { [key: string]: string | null | undefined }, argKey: string): boolean { +export function argKeyExists(map: { [key: string]: Array | null | undefined }, argKey: string): boolean { return map[argKey] !== undefined; } diff --git a/src/test/testing/testController/utils.unit.test.ts b/src/test/testing/testController/utils.unit.test.ts index 5bcf8dfa10c8..36ee42ab022e 100644 --- a/src/test/testing/testController/utils.unit.test.ts +++ b/src/test/testing/testController/utils.unit.test.ts @@ -165,10 +165,10 @@ ${data}${secondPayload}`; suite('Test Controller Utils: Args Mapping', () => { test('Converts map with mixed values to array of strings', async () => { const inputMap = { - key1: 'value1', + key1: ['value1'], key2: null, key3: undefined, - key4: 'value4', + key4: ['value4'], }; const expectedOutput = ['key1=value1', 'key2', 'key4=value4']; @@ -209,6 +209,17 @@ ${data}${secondPayload}`; assert.deepStrictEqual(result, expectedOutput); }); + test('Handles mapToArgs for a key with multiple values', async () => { + const inputMap = { + key1: null, + key2: ['value1', 'value2'], + }; + const expectedOutput = ['key1', 'key2=value1', 'key2=value2']; + + const result = mapToArgs(inputMap); + + assert.deepStrictEqual(result, expectedOutput); + }); test('Adds new argument if it does not exist', () => { const map = {}; const argKey = 'newKey'; @@ -216,17 +227,17 @@ ${data}${secondPayload}`; const updatedMap = addArgIfNotExist(map, argKey, argValue); - assert.deepStrictEqual(updatedMap, { [argKey]: argValue }); + assert.deepStrictEqual(updatedMap, { [argKey]: [argValue] }); }); test('Does not overwrite existing argument', () => { - const map = { existingKey: 'existingValue' }; + const map = { existingKey: ['existingValue'] }; const argKey = 'existingKey'; const argValue = 'newValue'; const updatedMap = addArgIfNotExist(map, argKey, argValue); - assert.deepStrictEqual(updatedMap, { [argKey]: 'existingValue' }); + assert.deepStrictEqual(updatedMap, { [argKey]: ['existingValue'] }); }); test('Handles null value for new key', () => { @@ -249,21 +260,9 @@ ${data}${secondPayload}`; assert.deepStrictEqual(updatedMap, { [argKey]: null }); }); - test('Accepts addition if key exists with undefined value', () => { - const map = { undefinedKey: undefined }; - const argKey = 'undefinedKey'; - const argValue = 'newValue'; - - // Attempting to add a key that is explicitly set to undefined - const updatedMap = addArgIfNotExist(map, argKey, argValue); - - // Expect the map to remain unchanged because the key exists as undefined - assert.strictEqual(map[argKey], argValue); - assert.deepStrictEqual(updatedMap, { [argKey]: argValue }); - }); test('Complex test for argKeyExists with various key types', () => { const map = { - stringKey: 'stringValue', + stringKey: ['stringValue'], nullKey: null, // Note: not adding an 'undefinedKey' explicitly since it's not present and hence undefined by default }; @@ -289,7 +288,15 @@ ${data}${secondPayload}`; }); test('Converts array of strings with "=" into a map', () => { const args = ['key1=value1', 'key2=value2']; - const expectedMap = { key1: 'value1', key2: 'value2' }; + const expectedMap = { key1: ['value1'], key2: ['value2'] }; + + const resultMap = argsToMap(args); + + assert.deepStrictEqual(resultMap, expectedMap); + }); + test('Handles argsToMap for multiple values for the same key', () => { + const args = ['key1=value1', 'key1=value2']; + const expectedMap = { key1: ['value1', 'value2'] }; const resultMap = argsToMap(args); @@ -307,7 +314,7 @@ ${data}${secondPayload}`; test('Handles mixed keys with and without "="', () => { const args = ['key1=value1', 'key2']; - const expectedMap = { key1: 'value1', key2: null }; + const expectedMap = { key1: ['value1'], key2: null }; const resultMap = argsToMap(args); @@ -316,7 +323,7 @@ ${data}${secondPayload}`; test('Handles strings with multiple "=" characters', () => { const args = ['key1=part1=part2']; - const expectedMap = { key1: 'part1=part2' }; + const expectedMap = { key1: ['part1=part2'] }; const resultMap = argsToMap(args); From c54841404c40ac30535a356e28d8bf35cc8f6992 Mon Sep 17 00:00:00 2001 From: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:49:11 -0600 Subject: [PATCH 044/767] Updating devcontainer.json and README.md (#22984) - Adding the Python Debugger extension to the `devcontainer.json` extensions. - Small hygiene updates and removing mention of built linting and formatting support on `README.md`. --------- Co-authored-by: Luciana Abud <45497113+luabud@users.noreply.github.com> --- .devcontainer/devcontainer.json | 3 ++- README.md | 24 ++++++++++++++++-------- package.json | 4 +--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fe15f35764e6..f123a5864932 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,7 +15,8 @@ "ms-python.python", "ms-python.black-formatter", "ms-python.vscode-pylance", - "charliermarsh.ruff" + "charliermarsh.ruff", + "ms-python.debugpy" ] } }, diff --git a/README.md b/README.md index 6ad9fd6c5e61..0db0ab73a55b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Python extension for Visual Studio Code -A [Visual Studio Code](https://code.visualstudio.com/) [extension](https://marketplace.visualstudio.com/VSCode) with rich support for the [Python language](https://www.python.org/) (for all [actively supported versions](https://devguide.python.org/#status-of-python-branches) of the language: >=3.7), including features such as IntelliSense (Pylance), linting, debugging (Python Debugger), code navigation, code formatting, refactoring, variable explorer, test explorer, and more! +A [Visual Studio Code](https://code.visualstudio.com/) [extension](https://marketplace.visualstudio.com/VSCode) with rich support for the [Python language](https://www.python.org/) (for all [actively supported Python versions](https://devguide.python.org/versions/#supported-versions)), providing access points for extensions to seamlessly integrate and offer support for IntelliSense (Pylance), debugging (Python Debugger), formatting, linting, code navigation, refactoring, variable explorer, test explorer, and more! ## Support for [vscode.dev](https://vscode.dev/) @@ -16,6 +16,15 @@ The Python extension will automatically install the following extensions by defa These extensions are optional dependencies, meaning the Python extension will remain fully functional if they fail to be installed. Any or all of these extensions can be [disabled](https://code.visualstudio.com/docs/editor/extension-marketplace#_disable-an-extension) or [uninstalled](https://code.visualstudio.com/docs/editor/extension-marketplace#_uninstall-an-extension) at the expense of some features. Extensions installed through the marketplace are subject to the [Marketplace Terms of Use](https://cdn.vsassets.io/v/M146_20190123.39/_content/Microsoft-Visual-Studio-Marketplace-Terms-of-Use.pdf). +## Extensibility + +The Python extension provides pluggable access points for extensions that extend various feature areas to further improve your Python development experience. These extensions are all optional and depend on your project configuration and preferences. + +- [Python formatters](https://code.visualstudio.com/docs/python/formatting#_choose-a-formatter) +- [Python linters](https://code.visualstudio.com/docs/python/linting#_choose-a-linter) + +If you encounter issues with any of the listed extensions, please file an issue in its corresponding repo. + ## Quick start - **Step 1.** [Install a supported version of Python on your system](https://code.visualstudio.com/docs/python/python-tutorial#_prerequisites) (note: that the system install of Python on macOS is not supported). @@ -63,7 +72,6 @@ Open the Command Palette (Command+Shift+P on macOS and Ctrl+Shift+P on Windows/L | `Python: Select Interpreter` | Switch between Python interpreters, versions, and environments. | | `Python: Start REPL` | Start an interactive Python REPL using the selected interpreter in the VS Code terminal. | | `Python: Run Python File in Terminal` | Runs the active Python file in the VS Code terminal. You can also run a Python file by right-clicking on the file and selecting `Run Python File in Terminal`. | -| `Format Document` | Formats code using the provided [formatter](https://code.visualstudio.com/docs/python/formatting) in the `settings.json` file. | | `Python: Configure Tests` | Select a test framework and configure it to display the Test Explorer. | To see all available Python commands, open the Command Palette and type `Python`. For Jupyter extension commands, just type `Jupyter`. @@ -89,13 +97,13 @@ The extension is available in multiple languages: `de`, `en`, `es`, `fa`, `fr`, ## Questions, issues, feature requests, and contributions -- If you have a question about how to accomplish something with the extension, please [ask on Stack Overflow](https://stackoverflow.com/questions/tagged/visual-studio-code+python) -- If you come across a problem with the extension, please [file an issue](https://github.com/microsoft/vscode-python) -- Contributions are always welcome! Please see our [contributing guide](https://github.com/Microsoft/vscode-python/blob/main/CONTRIBUTING.md) for more details +- If you have a question about how to accomplish something with the extension, please [ask on our Discussions page](https://github.com/microsoft/vscode-python/discussions/categories/q-a). +- If you come across a problem with the extension, please [file an issue](https://github.com/microsoft/vscode-python). +- Contributions are always welcome! Please see our [contributing guide](https://github.com/Microsoft/vscode-python/blob/main/CONTRIBUTING.md) for more details. - Any and all feedback is appreciated and welcome! - - If someone has already [filed an issue](https://github.com/Microsoft/vscode-python) that encompasses your feedback, please leave a 👍/👎 reaction on the issue - - Otherwise please start a [new discussion](https://github.com/microsoft/vscode-python/discussions/categories/ideas) -- If you're interested in the development of the extension, you can read about our [development process](https://github.com/Microsoft/vscode-python/blob/main/CONTRIBUTING.md#development-process) + - If someone has already [filed an issue](https://github.com/Microsoft/vscode-python) that encompasses your feedback, please leave a 👍/👎 reaction on the issue. + - Otherwise please start a [new discussion](https://github.com/microsoft/vscode-python/discussions/categories/ideas). +- If you're interested in the development of the extension, you can read about our [development process](https://github.com/Microsoft/vscode-python/blob/main/CONTRIBUTING.md#development-process). ## Data and telemetry diff --git a/package.json b/package.json index 134f302a38a4..a753336396e4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "python", "displayName": "Python", - "description": "IntelliSense (Pylance), Linting, Debugging (Python Debugger), code formatting, refactoring, unit tests, and more.", + "description": "Python language support with extension access points for IntelliSense (Pylance), Debugging (Python Debugger), linting, formatting, refactoring, unit tests, and more.", "version": "2024.3.0-dev", "featureFlags": { "usingNewInterpreterStorage": true @@ -56,8 +56,6 @@ "categories": [ "Programming Languages", "Debuggers", - "Linters", - "Formatters", "Other", "Data Science", "Machine Learning" From 0d1cfe87abe71839caa59995fa6fdde1a3388bac Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:40:15 -0800 Subject: [PATCH 045/767] Fix backed up command and false exit code (#23025) Resolves: #23016 #22942 --- python_files/pythonrc.py | 4 ++-- python_files/tests/test_shell_integration.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python_files/pythonrc.py b/python_files/pythonrc.py index 374888ddada5..2edd88874674 100644 --- a/python_files/pythonrc.py +++ b/python_files/pythonrc.py @@ -54,13 +54,13 @@ def __str__(self): result = "" # For non-windows allow recent_command history. if sys.platform != "win32": - result = "{command_finished}{prompt_started}{prompt}{command_start}{command_executed}{command_line}".format( + result = "{command_line}{command_finished}{prompt_started}{prompt}{command_start}{command_executed}".format( + command_line="\x1b]633;E;" + str(get_last_command()) + "\x07", command_finished="\x1b]633;D;" + str(exit_code) + "\x07", prompt_started="\x1b]633;A\x07", prompt=original_ps1, command_start="\x1b]633;B\x07", command_executed="\x1b]633;C\x07", - command_line="\x1b]633;E;" + str(get_last_command()) + "\x07", ) else: result = "{command_finished}{prompt_started}{prompt}{command_start}{command_executed}".format( diff --git a/python_files/tests/test_shell_integration.py b/python_files/tests/test_shell_integration.py index 896df416eced..c5911aad2d1d 100644 --- a/python_files/tests/test_shell_integration.py +++ b/python_files/tests/test_shell_integration.py @@ -13,7 +13,7 @@ def test_decoration_success(): if sys.platform != "win32": assert ( result - == "\x1b]633;D;0\x07\x1b]633;A\x07>>> \x1b]633;B\x07\x1b]633;C\x07\x1b]633;E;None\x07" + == "\x1b]633;E;None\x07\x1b]633;D;0\x07\x1b]633;A\x07>>> \x1b]633;B\x07\x1b]633;C\x07" ) else: pass @@ -28,7 +28,7 @@ def test_decoration_failure(): if sys.platform != "win32": assert ( result - == "\x1b]633;D;1\x07\x1b]633;A\x07>>> \x1b]633;B\x07\x1b]633;C\x07\x1b]633;E;None\x07" + == "\x1b]633;E;None\x07\x1b]633;D;1\x07\x1b]633;A\x07>>> \x1b]633;B\x07\x1b]633;C\x07" ) else: pass From 3609f28c5f6936301f4143e0a4406c6a08f1186a Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Wed, 6 Mar 2024 09:43:55 +0530 Subject: [PATCH 046/767] Do not use worker threads to launching binaries (#23030) --- .../base/locators/lowLevel/condaLocator.ts | 7 +---- .../lowLevel/windowsRegistryLocator.ts | 7 +---- .../common/environmentManagers/conda.ts | 14 ++------- .../common/externalDependencies.ts | 5 ++- .../windowsRegistryLocator.testvirtualenvs.ts | 31 ------------------- 5 files changed, 6 insertions(+), 58 deletions(-) delete mode 100644 src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts index 651a43ff8868..08622d758929 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/condaLocator.ts @@ -6,8 +6,6 @@ import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; import { Conda, getCondaEnvironmentsTxt } from '../../../common/environmentManagers/conda'; import { traceError, traceVerbose } from '../../../../logging'; import { FSWatchingLocator } from './fsWatchingLocator'; -import { DiscoveryUsingWorkers } from '../../../../common/experiments/groups'; -import { inExperiment } from '../../../common/externalDependencies'; export class CondaEnvironmentLocator extends FSWatchingLocator { public readonly providerId: string = 'conda-envs'; @@ -21,10 +19,7 @@ export class CondaEnvironmentLocator extends FSWatchingLocator { } // eslint-disable-next-line class-methods-use-this - public async *doIterEnvs( - _: unknown, - useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment), - ): IPythonEnvsIterator { + public async *doIterEnvs(_: unknown, useWorkerThreads = false): IPythonEnvsIterator { const conda = await Conda.getConda(undefined, useWorkerThreads); if (conda === undefined) { traceVerbose(`Couldn't locate the conda binary.`); diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts index 4f5a14721a19..90ea7ff5edd5 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.ts @@ -8,8 +8,6 @@ import { BasicEnvInfo, IPythonEnvsIterator, Locator, PythonLocatorQuery, IEmitte import { getRegistryInterpreters } from '../../../common/windowsUtils'; import { traceError, traceVerbose } from '../../../../logging'; import { isMicrosoftStoreDir } from '../../../common/environmentManagers/microsoftStoreEnv'; -import { inExperiment } from '../../../common/externalDependencies'; -import { DiscoveryUsingWorkers } from '../../../../common/experiments/groups'; import { PythonEnvsChangedEvent } from '../../watcher'; export const WINDOWS_REG_PROVIDER_ID = 'windows-registry'; @@ -18,10 +16,7 @@ export class WindowsRegistryLocator extends Locator { public readonly providerId: string = WINDOWS_REG_PROVIDER_ID; // eslint-disable-next-line class-methods-use-this - public iterEnvs( - query?: PythonLocatorQuery, - useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment), - ): IPythonEnvsIterator { + public iterEnvs(query?: PythonLocatorQuery, useWorkerThreads = false): IPythonEnvsIterator { if (useWorkerThreads) { /** * Windows registry is slow and often not necessary, so notify completion immediately, but use watcher diff --git a/src/client/pythonEnvironments/common/environmentManagers/conda.ts b/src/client/pythonEnvironments/common/environmentManagers/conda.ts index 1cb2e490aef8..f1941b0c5025 100644 --- a/src/client/pythonEnvironments/common/environmentManagers/conda.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/conda.ts @@ -10,7 +10,6 @@ import { readFile, onDidChangePythonSetting, exec, - inExperiment, } from '../externalDependencies'; import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../base/info'; @@ -25,7 +24,6 @@ import { OUTPUT_MARKER_SCRIPT } from '../../../common/process/internal/scripts'; import { splitLines } from '../../../common/stringUtils'; import { SpawnOptions } from '../../../common/process/types'; import { sleep } from '../../../common/utils/async'; -import { DiscoveryUsingWorkers } from '../../../common/experiments/groups'; export const AnacondaCompanyName = 'Anaconda, Inc.'; export const CONDAPATH_SETTING_KEY = 'condaPath'; @@ -274,11 +272,7 @@ export class Conda { private readonly useWorkerThreads?: boolean, ) { if (this.useWorkerThreads === undefined) { - try { - this.useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment); - } catch { - this.useWorkerThreads = false; // Temporarily support for legacy tests - } + this.useWorkerThreads = false; } this.shellCommand = shellCommand ?? command; onDidChangePythonSetting(CONDAPATH_SETTING_KEY, () => { @@ -302,11 +296,7 @@ export class Conda { private static async locate(shellPath?: string, useWorkerThread?: boolean): Promise { let useWorkerThreads: boolean; if (useWorkerThread === undefined) { - try { - useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment); - } catch { - useWorkerThreads = false; // Temporarily support for legacy tests - } + useWorkerThreads = false; } traceVerbose(`Searching for conda.`); const home = getUserHomeDir(); diff --git a/src/client/pythonEnvironments/common/externalDependencies.ts b/src/client/pythonEnvironments/common/externalDependencies.ts index 2ecbbdf0cb11..d6b774744a70 100644 --- a/src/client/pythonEnvironments/common/externalDependencies.ts +++ b/src/client/pythonEnvironments/common/externalDependencies.ts @@ -11,7 +11,6 @@ import { chain, iterable } from '../../common/utils/async'; import { getOSType, OSType } from '../../common/utils/platform'; import { IServiceContainer } from '../../ioc/types'; import { traceError, traceVerbose } from '../../logging'; -import { DiscoveryUsingWorkers } from '../../common/experiments/groups'; let internalServiceContainer: IServiceContainer; export function initializeExternalDependencies(serviceContainer: IServiceContainer): void { @@ -21,7 +20,7 @@ export function initializeExternalDependencies(serviceContainer: IServiceContain // processes export async function shellExecute(command: string, options: ShellOptions = {}): Promise> { - const useWorker = inExperiment(DiscoveryUsingWorkers.experiment); + const useWorker = false; const service = await internalServiceContainer.get(IProcessServiceFactory).create(); options = { ...options, useWorker }; return service.shellExec(command, options); @@ -31,7 +30,7 @@ export async function exec( file: string, args: string[], options: SpawnOptions = {}, - useWorker = inExperiment(DiscoveryUsingWorkers.experiment), + useWorker = false, ): Promise> { const service = await internalServiceContainer.get(IProcessServiceFactory).create(); options = { ...options, useWorker }; diff --git a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts b/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts deleted file mode 100644 index 110464356040..000000000000 --- a/src/test/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator.testvirtualenvs.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { getEnvs } from '../../../../../client/pythonEnvironments/base/locatorUtils'; -import { - WindowsRegistryLocator, - WINDOWS_REG_PROVIDER_ID, -} from '../../../../../client/pythonEnvironments/base/locators/lowLevel/windowsRegistryLocator'; -import { assertBasicEnvsEqual } from '../envTestUtils'; -import { TEST_TIMEOUT } from '../../../../constants'; -import { getOSType, OSType } from '../../../../../client/common/utils/platform'; - -suite('Windows Registry Locator', async () => { - let locator: WindowsRegistryLocator; - - setup(function () { - if (getOSType() !== OSType.Windows) { - return this.skip(); - } - locator = new WindowsRegistryLocator(); - return undefined; - }); - - test('Worker thread to fetch registry interpreters is working', async () => { - const items = await getEnvs(locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, false)); - const workerItems = await getEnvs(locator.iterEnvs({ providerId: WINDOWS_REG_PROVIDER_ID }, true)); - console.log('Number of items Windows registry locator returned:', items.length); - // Make sure items returned when using worker threads v/s not are the same. - assertBasicEnvsEqual(items, workerItems); - }).timeout(TEST_TIMEOUT * 2); -}); From 209d6bd69699220bc56666a938798c8549baf933 Mon Sep 17 00:00:00 2001 From: Erik De Bonte Date: Thu, 7 Mar 2024 07:58:18 -0800 Subject: [PATCH 047/767] Add `server_side_request` to `telemetry\pylance.ts` (#23031) --- src/client/telemetry/pylance.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client/telemetry/pylance.ts b/src/client/telemetry/pylance.ts index 67be7428c7be..c0a3edb013c9 100644 --- a/src/client/telemetry/pylance.ts +++ b/src/client/telemetry/pylance.ts @@ -27,7 +27,8 @@ "errorstack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, "lsversion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "method" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "modulehash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "modulehash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "resultLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ /* __GDPR__ @@ -313,6 +314,15 @@ "typeevaltime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ +/* __GDPR__ + "language_server/server_side_request" : { + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "lsversion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "method" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "modulehash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "resultLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } +*/ /* __GDPR__ "language_server/settings" : { "addimportexactmatchonly" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, From 7ea558429178608ccc1e08b8d577c88792598972 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 12 Mar 2024 12:24:59 -0700 Subject: [PATCH 048/767] Add linting rules for ruff (#22741) Co-authored-by: Karthik Nadig Co-authored-by: eleanorjboyd --- .github/actions/lint/action.yml | 3 +- python_files/installed_check.py | 6 +- python_files/pyproject.toml | 30 +--------- python_files/run-jedi-language-server.py | 4 +- python_files/testing_tools/adapter/util.py | 16 +---- .../testing_tools/unittest_discovery.py | 13 ++-- .../debug_adapter/test_install_debugpy.py | 3 - python_files/tests/pytestadapter/helpers.py | 6 +- .../tests/pytestadapter/test_discovery.py | 59 +++++++++---------- python_files/tests/run_all.py | 3 +- .../testing_tools/adapter/test_discovery.py | 2 +- .../tests/testing_tools/adapter/test_util.py | 1 - .../tests/unittestadapter/test_discovery.py | 6 +- .../tests/unittestadapter/test_execution.py | 2 +- .../tests/unittestadapter/test_utils.py | 3 +- python_files/unittestadapter/discovery.py | 7 ++- python_files/unittestadapter/execution.py | 7 ++- python_files/unittestadapter/pvsc_utils.py | 2 +- python_files/visualstudio_py_testlauncher.py | 34 ++++------- .../tests/logParser.py | 5 +- python_files/vscode_pytest/__init__.py | 8 +-- .../vscode_pytest/run_pytest_script.py | 2 +- 22 files changed, 82 insertions(+), 140 deletions(-) diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index ed0e380bed6e..97f19814b362 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -43,7 +43,8 @@ runs: - name: Check Python format run: | - python -m pip install -U black + python -m pip install -U black ruff + python -m ruff check python -m black . --check working-directory: python_files shell: bash diff --git a/python_files/installed_check.py b/python_files/installed_check.py index 4a43a8bc8b30..6dafe23b5121 100644 --- a/python_files/installed_check.py +++ b/python_files/installed_check.py @@ -11,9 +11,9 @@ LIB_ROOT = pathlib.Path(__file__).parent / "lib" / "python" sys.path.insert(0, os.fspath(LIB_ROOT)) -import tomli -from importlib_metadata import metadata -from packaging.requirements import Requirement +import tomli # noqa: E402 +from importlib_metadata import metadata # noqa: E402 +from packaging.requirements import Requirement # noqa: E402 DEFAULT_SEVERITY = "3" # 'Hint' try: diff --git a/python_files/pyproject.toml b/python_files/pyproject.toml index 151e598bd2b2..94353be0b644 100644 --- a/python_files/pyproject.toml +++ b/python_files/pyproject.toml @@ -37,31 +37,7 @@ ignore = [ [tool.ruff] line-length = 140 -ignore = ["E402"] -exclude = [ - # Ignore testing_tools files same as Pyright way - 'get-pip.py', - 'install_debugpy.py', - 'tensorboard_launcher.py', - 'testlauncher.py', - 'visualstudio_py_testlauncher.py', - 'testing_tools/unittest_discovery.py', - 'testing_tools/adapter/util.py', - 'testing_tools/adapter/pytest/_discovery.py', - 'testing_tools/adapter/pytest/_pytest_item.py', - 'tests/debug_adapter/test_install_debugpy.py', - 'tests/testing_tools/adapter/.data', - 'tests/testing_tools/adapter/test___main__.py', - 'tests/testing_tools/adapter/test_discovery.py', - 'tests/testing_tools/adapter/test_functional.py', - 'tests/testing_tools/adapter/test_report.py', - 'tests/testing_tools/adapter/test_util.py', - 'tests/testing_tools/adapter/pytest/test_cli.py', - 'tests/testing_tools/adapter/pytest/test_discovery.py', - 'python_files/testing_tools/*', - 'python_files/testing_tools/adapter/pytest/__init__.py', - 'python_files/tests/pytestadapter/expected_execution_test_output.py', - 'python_files/tests/unittestadapter/.data/discovery_error/file_one.py', - 'python_files/tests/unittestadapter/test_utils.py', +exclude = ["tests/testing_tools/adapter/.data"] -] +[tool.ruff.lint.pydocstyle] +convention = "pep257" diff --git a/python_files/run-jedi-language-server.py b/python_files/run-jedi-language-server.py index 3239bc7e9c8c..5a972799bc33 100644 --- a/python_files/run-jedi-language-server.py +++ b/python_files/run-jedi-language-server.py @@ -1,11 +1,11 @@ -import sys import os +import sys # Add the lib path to our sys path so jedi_language_server can find its references EXTENSION_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.join(EXTENSION_ROOT, "python_files", "lib", "jedilsp")) -from jedi_language_server.cli import cli +from jedi_language_server.cli import cli # noqa: E402 sys.exit(cli()) diff --git a/python_files/testing_tools/adapter/util.py b/python_files/testing_tools/adapter/util.py index c7a178311b8b..4799980db53e 100644 --- a/python_files/testing_tools/adapter/util.py +++ b/python_files/testing_tools/adapter/util.py @@ -46,12 +46,6 @@ def group_attr_names(attrnames): return grouped -if sys.version_info < (3,): - _str_to_lower = lambda val: val.decode().lower() -else: - _str_to_lower = str.lower - - ############################# # file paths @@ -164,7 +158,7 @@ def fix_fileid( if normalize: if strictpathsep: raise ValueError("cannot normalize *and* keep strict path separator") - _fileid = _str_to_lower(_fileid) + _fileid = _fileid.lower() elif strictpathsep: # We do not use _normcase since we want to preserve capitalization. _fileid = _fileid.replace("/", _pathsep) @@ -224,12 +218,6 @@ def _replace_stderr(target): sys.stderr = orig -if sys.version_info < (3,): - _coerce_unicode = lambda s: unicode(s) -else: - _coerce_unicode = lambda s: s - - @contextlib.contextmanager def _temp_io(): sio = StringIO() @@ -239,7 +227,7 @@ def _temp_io(): finally: tmp.seek(0) buff = tmp.read() - sio.write(_coerce_unicode(buff)) + sio.write(buff) @contextlib.contextmanager diff --git a/python_files/testing_tools/unittest_discovery.py b/python_files/testing_tools/unittest_discovery.py index 2988092c387c..5d5e9bcc6601 100644 --- a/python_files/testing_tools/unittest_discovery.py +++ b/python_files/testing_tools/unittest_discovery.py @@ -1,3 +1,4 @@ +import contextlib import inspect import os import sys @@ -13,13 +14,13 @@ def get_sourceline(obj): try: s, n = inspect.getsourcelines(obj) - except: + except Exception: try: # this handles `tornado` case we need a better # way to get to the wrapped function. - # This is a temporary solution + # XXX This is a temporary solution s, n = inspect.getsourcelines(obj.orig_method) - except: + except Exception: return "*" for i, v in enumerate(s): @@ -50,16 +51,14 @@ def generate_test_cases(suite): loader_errors.append(s._exception) else: print(testId.replace(".", ":") + ":" + get_sourceline(tm)) -except: +except Exception: print("=== exception start ===") traceback.print_exc() print("=== exception end ===") for error in loader_errors: - try: + with contextlib.suppress(Exception): print("=== exception start ===") print(error.msg) print("=== exception end ===") - except: - pass diff --git a/python_files/tests/debug_adapter/test_install_debugpy.py b/python_files/tests/debug_adapter/test_install_debugpy.py index 8e2ed33a1daf..45eff1272e00 100644 --- a/python_files/tests/debug_adapter/test_install_debugpy.py +++ b/python_files/tests/debug_adapter/test_install_debugpy.py @@ -1,7 +1,4 @@ import os -import pytest -import subprocess -import sys def _check_binaries(dir_path): diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index a3ed21cc5538..8657da612954 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -11,14 +11,10 @@ import sys import threading import uuid -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, TypedDict -script_dir = pathlib.Path(__file__).parent.parent.parent -sys.path.append(os.fspath(script_dir)) -sys.path.append(os.fspath(script_dir / "lib" / "python")) TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" -from typing_extensions import TypedDict def get_absolute_test_id(test_id: str, testPath: pathlib.Path) -> str: diff --git a/python_files/tests/pytestadapter/test_discovery.py b/python_files/tests/pytestadapter/test_discovery.py index a1f4e4f266ae..42795fb7920f 100644 --- a/python_files/tests/pytestadapter/test_discovery.py +++ b/python_files/tests/pytestadapter/test_discovery.py @@ -2,20 +2,15 @@ # Licensed under the MIT License. import json import os -import pathlib import shutil import sys from typing import Any, Dict, List, Optional import pytest -script_dir = pathlib.Path(__file__).parent.parent -sys.path.append(os.fspath(script_dir)) +from tests.tree_comparison_helper import is_same_tree # noqa: E402 -from tests.tree_comparison_helper import is_same_tree - -from . import expected_discovery_test_output -from .helpers import TEST_DATA_PATH, runner, runner_with_cwd, create_symlink +from . import expected_discovery_test_output, helpers # noqa: E402 @pytest.mark.skipif( @@ -36,12 +31,14 @@ def test_import_error(tmp_path): # Saving some files as .txt to avoid that file displaying a syntax error for # the extension as a whole. Instead, rename it before running this test # in order to test the error handling. - file_path = TEST_DATA_PATH / "error_pytest_import.txt" + file_path = helpers.TEST_DATA_PATH / "error_pytest_import.txt" temp_dir = tmp_path / "temp_data" temp_dir.mkdir() p = temp_dir / "error_pytest_import.py" shutil.copyfile(file_path, p) - actual: Optional[List[Dict[str, Any]]] = runner(["--collect-only", os.fspath(p)]) + actual: Optional[List[Dict[str, Any]]] = helpers.runner( + ["--collect-only", os.fspath(p)] + ) assert actual actual_list: List[Dict[str, Any]] = actual if actual_list is not None: @@ -51,7 +48,7 @@ def test_import_error(tmp_path): item in actual_item.keys() for item in ("status", "cwd", "error") ) assert actual_item.get("status") == "error" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) # Ensure that 'error' is a list and then check its length error_content = actual_item.get("error") @@ -81,12 +78,12 @@ def test_syntax_error(tmp_path): # Saving some files as .txt to avoid that file displaying a syntax error for # the extension as a whole. Instead, rename it before running this test # in order to test the error handling. - file_path = TEST_DATA_PATH / "error_syntax_discovery.txt" + file_path = helpers.TEST_DATA_PATH / "error_syntax_discovery.txt" temp_dir = tmp_path / "temp_data" temp_dir.mkdir() p = temp_dir / "error_syntax_discovery.py" shutil.copyfile(file_path, p) - actual = runner(["--collect-only", os.fspath(p)]) + actual = helpers.runner(["--collect-only", os.fspath(p)]) assert actual actual_list: List[Dict[str, Any]] = actual if actual_list is not None: @@ -96,7 +93,7 @@ def test_syntax_error(tmp_path): item in actual_item.keys() for item in ("status", "cwd", "error") ) assert actual_item.get("status") == "error" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) # Ensure that 'error' is a list and then check its length error_content = actual_item.get("error") @@ -114,7 +111,7 @@ def test_parameterized_error_collect(): The json should still be returned but the errors list should be present. """ file_path_str = "error_parametrize_discovery.py" - actual = runner(["--collect-only", file_path_str]) + actual = helpers.runner(["--collect-only", file_path_str]) assert actual actual_list: List[Dict[str, Any]] = actual if actual_list is not None: @@ -124,7 +121,7 @@ def test_parameterized_error_collect(): item in actual_item.keys() for item in ("status", "cwd", "error") ) assert actual_item.get("status") == "error" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) # Ensure that 'error' is a list and then check its length error_content = actual_item.get("error") @@ -196,10 +193,10 @@ def test_pytest_collect(file, expected_const): file -- a string with the file or folder to run pytest discovery on. expected_const -- the expected output from running pytest discovery on the file. """ - actual = runner( + actual = helpers.runner( [ "--collect-only", - os.fspath(TEST_DATA_PATH / file), + os.fspath(helpers.TEST_DATA_PATH / file), ] ) @@ -210,8 +207,10 @@ def test_pytest_collect(file, expected_const): actual_item = actual_list.pop(0) assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) - assert is_same_tree(actual_item.get("tests"), expected_const) + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) + assert not is_same_tree( + actual_item.get("tests"), expected_const + ), f"Tests tree does not match expected value. \n Expected: {json.dumps(expected_const, indent=4)}. \n Actual: {json.dumps(actual_item.get('tests'), indent=4)}" def test_symlink_root_dir(): @@ -219,14 +218,14 @@ def test_symlink_root_dir(): Test to test pytest discovery with the command line arg --rootdir specified as a symlink path. Discovery should succeed and testids should be relative to the symlinked root directory. """ - with create_symlink(TEST_DATA_PATH, "root", "symlink_folder") as ( + with helpers.create_symlink(helpers.TEST_DATA_PATH, "root", "symlink_folder") as ( source, destination, ): assert destination.is_symlink() # Run pytest with the cwd being the resolved symlink path (as it will be when we run the subprocess from node). - actual = runner_with_cwd( + actual = helpers.runner_with_cwd( ["--collect-only", f"--rootdir={os.fspath(destination)}"], source ) expected = expected_discovery_test_output.symlink_expected_discovery_output @@ -258,13 +257,13 @@ def test_pytest_root_dir(): Test to test pytest discovery with the command line arg --rootdir specified to be a subfolder of the workspace root. Discovery should succeed and testids should be relative to workspace root. """ - rd = f"--rootdir={TEST_DATA_PATH / 'root' / 'tests'}" - actual = runner_with_cwd( + rd = f"--rootdir={helpers.TEST_DATA_PATH / 'root' / 'tests'}" + actual = helpers.runner_with_cwd( [ "--collect-only", rd, ], - TEST_DATA_PATH / "root", + helpers.TEST_DATA_PATH / "root", ) assert actual actual_list: List[Dict[str, Any]] = actual @@ -273,11 +272,11 @@ def test_pytest_root_dir(): actual_item = actual_list.pop(0) assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH / "root") + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH / "root") assert is_same_tree( actual_item.get("tests"), expected_discovery_test_output.root_with_config_expected_output, - ) + ), f"Tests tree does not match expected value. \n Expected: {json.dumps(expected_discovery_test_output.root_with_config_expected_output, indent=4)}. \n Actual: {json.dumps(actual_item.get('tests'), indent=4)}" def test_pytest_config_file(): @@ -285,12 +284,12 @@ def test_pytest_config_file(): Test to test pytest discovery with the command line arg -c with a specified config file which changes the workspace root. Discovery should succeed and testids should be relative to workspace root. """ - actual = runner_with_cwd( + actual = helpers.runner_with_cwd( [ "--collect-only", "tests/", ], - TEST_DATA_PATH / "root", + helpers.TEST_DATA_PATH / "root", ) assert actual actual_list: List[Dict[str, Any]] = actual @@ -299,8 +298,8 @@ def test_pytest_config_file(): actual_item = actual_list.pop(0) assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "success" - assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH / "root") + assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH / "root") assert is_same_tree( actual_item.get("tests"), expected_discovery_test_output.root_with_config_expected_output, - ) + ), f"Tests tree does not match expected value. \n Expected: {json.dumps(expected_discovery_test_output.root_with_config_expected_output, indent=4)}. \n Actual: {json.dumps(actual_item.get('tests'), indent=4)}" diff --git a/python_files/tests/run_all.py b/python_files/tests/run_all.py index ce5a62649962..7c864ba7c5c1 100644 --- a/python_files/tests/run_all.py +++ b/python_files/tests/run_all.py @@ -7,8 +7,7 @@ sys.path[0] = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -from tests.__main__ import main, parse_args - +from tests.__main__ import main, parse_args # noqa: E402 if __name__ == "__main__": mainkwargs, pytestargs = parse_args() diff --git a/python_files/tests/testing_tools/adapter/test_discovery.py b/python_files/tests/testing_tools/adapter/test_discovery.py index cf3b8fb3139b..c01e625becde 100644 --- a/python_files/tests/testing_tools/adapter/test_discovery.py +++ b/python_files/tests/testing_tools/adapter/test_discovery.py @@ -5,7 +5,7 @@ import unittest -from testing_tools.adapter.discovery import DiscoveredTests, fix_nodeid +from testing_tools.adapter.discovery import DiscoveredTests from testing_tools.adapter.info import ParentInfo, SingleTestInfo, SingleTestPath from testing_tools.adapter.util import fix_path, fix_relpath diff --git a/python_files/tests/testing_tools/adapter/test_util.py b/python_files/tests/testing_tools/adapter/test_util.py index 822ba2ed1b22..3fcafd1a93d5 100644 --- a/python_files/tests/testing_tools/adapter/test_util.py +++ b/python_files/tests/testing_tools/adapter/test_util.py @@ -11,7 +11,6 @@ import sys import unittest -import pytest # Pytest 3.7 and later uses pathlib/pathlib2 for path resolution. try: diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index a68774d3f2dc..fadc2aa3f7e2 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -7,6 +7,7 @@ from typing import List import pytest + from unittestadapter.discovery import discover_tests from unittestadapter.pvsc_utils import TestNodeTypeEnum, parse_unittest_args @@ -14,8 +15,9 @@ sys.path.append(os.fspath(script_dir)) -from . import expected_discovery_test_output -from tests.tree_comparison_helper import is_same_tree +from tests.tree_comparison_helper import is_same_tree # noqa: E402 + +from . import expected_discovery_test_output # noqa: E402 TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index 7d11c656b57b..519c13bc2e5d 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -10,7 +10,7 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from unittestadapter.execution import run_tests +from unittestadapter.execution import run_tests # noqa: E402 TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" diff --git a/python_files/tests/unittestadapter/test_utils.py b/python_files/tests/unittestadapter/test_utils.py index d5f6fbbe9f18..f650f12252f7 100644 --- a/python_files/tests/unittestadapter/test_utils.py +++ b/python_files/tests/unittestadapter/test_utils.py @@ -19,8 +19,7 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) -from tests.tree_comparison_helper import is_same_tree - +from tests.tree_comparison_helper import is_same_tree # noqa: E402 TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index db06004e02c9..a8dc8ed38097 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -13,11 +13,12 @@ sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from testing_tools import socket_manager -from typing_extensions import Literal, NotRequired, TypedDict +from typing_extensions import Literal, NotRequired, TypedDict # noqa: E402 + +from testing_tools import socket_manager # noqa: E402 # If I use from utils then there will be an import error in test_discovery.py. -from unittestadapter.pvsc_utils import ( +from unittestadapter.pvsc_utils import ( # noqa: E402 TestNode, build_test_tree, parse_unittest_args, diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 22451c25bf1f..f433c117ec7c 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -17,9 +17,10 @@ sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from testing_tools import process_json_util, socket_manager -from typing_extensions import Literal, NotRequired, TypeAlias, TypedDict -from unittestadapter.pvsc_utils import parse_unittest_args +from typing_extensions import Literal, NotRequired, TypeAlias, TypedDict # noqa: E402 + +from testing_tools import process_json_util, socket_manager # noqa: E402 +from unittestadapter.pvsc_utils import parse_unittest_args # noqa: E402 ErrorType = Union[ Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None] diff --git a/python_files/unittestadapter/pvsc_utils.py b/python_files/unittestadapter/pvsc_utils.py index 5632e69b09c7..673da2aa1eec 100644 --- a/python_files/unittestadapter/pvsc_utils.py +++ b/python_files/unittestadapter/pvsc_utils.py @@ -14,7 +14,7 @@ sys.path.append(os.fspath(script_dir)) sys.path.append(os.fspath(script_dir / "lib" / "python")) -from typing_extensions import TypedDict +from typing_extensions import TypedDict # noqa: E402 # Types diff --git a/python_files/visualstudio_py_testlauncher.py b/python_files/visualstudio_py_testlauncher.py index 0b0ef3242f65..854a9f4fae5a 100644 --- a/python_files/visualstudio_py_testlauncher.py +++ b/python_files/visualstudio_py_testlauncher.py @@ -17,6 +17,7 @@ __author__ = "Microsoft Corporation " __version__ = "3.0.0.0" +import contextlib import json import os import signal @@ -27,7 +28,7 @@ try: import thread -except: +except ModuleNotFoundError: import _thread as thread @@ -115,7 +116,7 @@ def close(self): def readSocket(self): try: - data = self.socket.recv(1024) + self.socket.recv(1024) self.callback() except OSError: if not self._closed: @@ -199,11 +200,8 @@ def sendResult(self, test, outcome, trace=None, subtest=None): def stopTests(): try: os.kill(os.getpid(), signal.SIGUSR1) - except: - try: - os.kill(os.getpid(), signal.SIGTERM) - except: - pass + except Exception: + os.kill(os.getpid(), signal.SIGTERM) class ExitCommand(Exception): @@ -280,11 +278,9 @@ def main(): if opts.result_port: try: signal.signal(signal.SIGUSR1, signal_handler) - except: - try: + except Exception: + with contextlib.suppress(Exception): signal.signal(signal.SIGTERM, signal_handler) - except: - pass _channel = _IpcChannel( socket.create_connection(("127.0.0.1", opts.result_port)), stopTests ) @@ -314,14 +310,12 @@ def main(): cov = None try: if opts.coverage: - try: + with contextlib.suppress(Exception): import coverage cov = coverage.coverage(opts.coverage) cov.load() cov.start() - except: - pass if opts.tests is None and opts.testFile is None: if opts.us is None: opts.us = "." @@ -345,7 +339,7 @@ def main(): # Run a specific test class or test method for test_suite in suites._tests: for cls in test_suite._tests: - try: + with contextlib.suppress(Exception): for m in cls._tests: testId = m.id() if testId.startswith(opts.tests[0]): @@ -355,8 +349,6 @@ def main(): tests = unittest.TestSuite([m]) else: tests.addTest(m) - except Exception as err: - errorMessage = traceback.format_exc() if tests is None: tests = suite if tests is None and suite is None: @@ -390,14 +382,10 @@ def main(): _channel.send_event(name="done") _channel.socket.close() # prevent generation of the error 'Error in sys.exitfunc:' - try: + with contextlib.suppress(Exception): sys.stdout.close() - except: - pass - try: + with contextlib.suppress(Exception): sys.stderr.close() - except: - pass if __name__ == "__main__": diff --git a/python_files/vscode_datascience_helpers/tests/logParser.py b/python_files/vscode_datascience_helpers/tests/logParser.py index e021853fee7a..a8132df8524f 100644 --- a/python_files/vscode_datascience_helpers/tests/logParser.py +++ b/python_files/vscode_datascience_helpers/tests/logParser.py @@ -1,10 +1,11 @@ import argparse import os +import re from io import TextIOWrapper +from pathlib import Path os.system("color") -import re -from pathlib import Path + parser = argparse.ArgumentParser(description="Parse a test log into its parts") parser.add_argument("testlog", type=str, nargs=1, help="Log to parse") diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index bdeefde469f4..05ac01fb90f2 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -10,14 +10,10 @@ import pytest -script_dir = pathlib.Path(__file__).parent.parent -sys.path.append(os.fspath(script_dir)) -sys.path.append(os.fspath(script_dir / "lib" / "python")) +from typing import Any, Dict, List, Optional, Union, Literal, TypedDict # noqa: E402 -from typing import Any, Dict, List, Optional, Union -from testing_tools import socket_manager -from typing_extensions import Literal, TypedDict +from testing_tools import socket_manager # noqa: E402 DEFAULT_PORT = 45454 diff --git a/python_files/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py index d6381e9b520b..fe7de8a45921 100644 --- a/python_files/vscode_pytest/run_pytest_script.py +++ b/python_files/vscode_pytest/run_pytest_script.py @@ -11,7 +11,7 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) sys.path.append(os.fspath(script_dir / "lib" / "python")) -from testing_tools import process_json_util +from testing_tools import process_json_util # noqa: E402 # This script handles running pytest via pytest.main(). It is called via run in the # pytest execution adapter and gets the test_ids to run via stdin and the rest of the From e931bed3efbede7b05113316506958ecd7506777 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 12 Mar 2024 15:35:53 -0700 Subject: [PATCH 049/767] Move to Ruff for formatting (#23060) Also set the line length to 100. --- .devcontainer/devcontainer.json | 3 +- .github/actions/lint/action.yml | 9 +- .vscode/extensions.json | 6 +- .vscode/settings.json | 2 +- python_files/linter.py | 6 +- python_files/normalizeSelection.py | 8 +- python_files/pyproject.toml | 10 +- .../testing_tools/adapter/__main__.py | 4 +- python_files/testing_tools/adapter/info.py | 4 +- .../adapter/pytest/_discovery.py | 2 +- .../adapter/pytest/_pytest_item.py | 4 +- python_files/testing_tools/adapter/report.py | 2 +- python_files/testing_tools/adapter/util.py | 4 +- python_files/testing_tools/socket_manager.py | 4 +- python_files/tests/__main__.py | 4 +- .../debug_adapter/test_install_debugpy.py | 8 +- .../expected_discovery_test_output.py | 37 ++---- .../expected_execution_test_output.py | 118 +++++------------- python_files/tests/pytestadapter/helpers.py | 20 +-- .../tests/pytestadapter/test_discovery.py | 20 +-- .../tests/pytestadapter/test_execution.py | 36 ++---- python_files/tests/test_create_conda.py | 4 +- python_files/tests/test_create_venv.py | 16 +-- .../adapter/pytest/test_discovery.py | 66 ++++------ .../testing_tools/adapter/test_discovery.py | 4 +- .../testing_tools/adapter/test_functional.py | 4 +- .../tests/testing_tools/adapter/test_util.py | 4 +- .../.data/two_patterns/pattern_a_test.py | 3 +- .../.data/two_patterns/test_pattern_b.py | 4 +- .../.data/unittest_folder/test_add.py | 9 +- .../.data/unittest_folder/test_subtract.py | 4 +- .../expected_discovery_test_output.py | 36 ++---- .../tests/unittestadapter/test_discovery.py | 36 +----- python_files/unittestadapter/discovery.py | 8 +- python_files/unittestadapter/execution.py | 16 +-- python_files/unittestadapter/pvsc_utils.py | 12 +- python_files/visualstudio_py_testlauncher.py | 24 +--- .../tests/logParser.py | 6 +- python_files/vscode_pytest/__init__.py | 25 +--- .../vscode_pytest/run_pytest_script.py | 8 +- 40 files changed, 162 insertions(+), 438 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f123a5864932..67a8833d30cf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,13 +9,12 @@ "customizations": { "vscode": { "extensions": [ + "charliermarsh.ruff", "editorconfig.editorconfig", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "ms-python.python", - "ms-python.black-formatter", "ms-python.vscode-pylance", - "charliermarsh.ruff", "ms-python.debugpy" ] } diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index 97f19814b362..444f331a3a96 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -41,17 +41,10 @@ runs: python-version: '3.x' cache: 'pip' - - name: Check Python format - run: | - python -m pip install -U black ruff - python -m ruff check - python -m black . --check - working-directory: python_files - shell: bash - - name: Run Ruff run: | python -m pip install -U ruff python -m ruff check . + python -m ruff format --check working-directory: python_files shell: bash diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 93a73827e7a2..15e6aada1d50 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,13 +2,11 @@ // See https://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ + "charliermarsh.ruff", "editorconfig.editorconfig", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "ms-python.python", - "ms-python.black-formatter", - "ms-python.vscode-pylance", - "ms-python.isort", - "ms-python.flake8" + "ms-python.vscode-pylance" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 5e5954991562..89959f33b6b1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -28,7 +28,7 @@ "source.fixAll.eslint": "explicit", "source.organizeImports.isort": "explicit" }, - "editor.defaultFormatter": "ms-python.black-formatter", + "editor.defaultFormatter": "charliermarsh.ruff", }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", diff --git a/python_files/linter.py b/python_files/linter.py index 58ad9397f58b..af9634f83f4b 100644 --- a/python_files/linter.py +++ b/python_files/linter.py @@ -37,11 +37,7 @@ def main(): invoke = sys.argv[1] if invoke == "-m": linter = sys.argv[2] - args = ( - [sys.executable, "-m", linter] - + linter_settings[linter]["args"] - + sys.argv[3:] - ) + args = [sys.executable, "-m", linter] + linter_settings[linter]["args"] + sys.argv[3:] else: linter = sys.argv[2] args = [sys.argv[3]] + linter_settings[linter]["args"] + sys.argv[4:] diff --git a/python_files/normalizeSelection.py b/python_files/normalizeSelection.py index 7608ce8860f6..7c65575c1dd8 100644 --- a/python_files/normalizeSelection.py +++ b/python_files/normalizeSelection.py @@ -191,9 +191,7 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted): ast.IfExp, ast.ExceptHandler, ) - if isinstance(node, ast_types_with_nodebody) and isinstance( - node.body, Iterable - ): + if isinstance(node, ast_types_with_nodebody) and isinstance(node.body, Iterable): for child_nodes in node.body: top_level_nodes.append(child_nodes) @@ -204,9 +202,7 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted): which_line_next = 0 for same_line_node in exact_nodes: should_run_top_blocks.append(same_line_node) - smart_code += ( - f"{ast.get_source_segment(wholeFileContent, same_line_node)}\n" - ) + smart_code += f"{ast.get_source_segment(wholeFileContent, same_line_node)}\n" which_line_next = get_next_block_lineno(should_run_top_blocks) return { "normalized_smart_result": smart_code, diff --git a/python_files/pyproject.toml b/python_files/pyproject.toml index 94353be0b644..9e8f55910e70 100644 --- a/python_files/pyproject.toml +++ b/python_files/pyproject.toml @@ -36,8 +36,14 @@ ignore = [ ] [tool.ruff] -line-length = 140 -exclude = ["tests/testing_tools/adapter/.data"] +line-length = 100 +exclude = [ + "tests/testing_tools/adapter/.data", + "tests/unittestadapter/.data" +] + +[tool.ruff.format] +docstring-code-format = true [tool.ruff.lint.pydocstyle] convention = "pep257" diff --git a/python_files/testing_tools/adapter/__main__.py b/python_files/testing_tools/adapter/__main__.py index 218456897df6..cc7084eb9439 100644 --- a/python_files/testing_tools/adapter/__main__.py +++ b/python_files/testing_tools/adapter/__main__.py @@ -50,9 +50,7 @@ def parse_args( subsub = add_subparser(cmdname, toolname, subsubs) if cmdname == "discover": subsub.add_argument("--simple", action="store_true") - subsub.add_argument( - "--no-hide-stdio", dest="hidestdio", action="store_false" - ) + subsub.add_argument("--no-hide-stdio", dest="hidestdio", action="store_false") subsub.add_argument("--pretty", action="store_true") # Parse the args! diff --git a/python_files/testing_tools/adapter/info.py b/python_files/testing_tools/adapter/info.py index d518a29dd97a..8e5d0442ce15 100644 --- a/python_files/testing_tools/adapter/info.py +++ b/python_files/testing_tools/adapter/info.py @@ -61,9 +61,7 @@ def __init__(self, *args, **kwargs): raise TypeError("missing relpath") -class SingleTestInfo( - namedtuple("TestInfo", "id name path source markers parentid kind") -): +class SingleTestInfo(namedtuple("TestInfo", "id name path source markers parentid kind")): """Info for a single test.""" MARKERS = ("skip", "skip-if", "expected-failure") diff --git a/python_files/testing_tools/adapter/pytest/_discovery.py b/python_files/testing_tools/adapter/pytest/_discovery.py index 4b852ecf81c9..bbe5ae9856c8 100644 --- a/python_files/testing_tools/adapter/pytest/_discovery.py +++ b/python_files/testing_tools/adapter/pytest/_discovery.py @@ -17,7 +17,7 @@ def discover( # *, _pytest_main=pytest.main, _plugin=None, - **_ignored + **_ignored, ): """Return the results of test discovery.""" if _plugin is None: diff --git a/python_files/testing_tools/adapter/pytest/_pytest_item.py b/python_files/testing_tools/adapter/pytest/_pytest_item.py index ccfe14122316..724b71a1ac44 100644 --- a/python_files/testing_tools/adapter/pytest/_pytest_item.py +++ b/python_files/testing_tools/adapter/pytest/_pytest_item.py @@ -170,9 +170,7 @@ def parse_item( parents = [(parentid, item.originalname, kind)] + parents name = parameterized[1:-1] or "" else: - (nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id( - item.nodeid, kind - ) + (nodeid, parents, fileid, testfunc, parameterized) = _parse_node_id(item.nodeid, kind) name = item.name # Note: testfunc does not necessarily match item.function.__name__. diff --git a/python_files/testing_tools/adapter/report.py b/python_files/testing_tools/adapter/report.py index bacdef7b9a00..1ad02fe7bcd4 100644 --- a/python_files/testing_tools/adapter/report.py +++ b/python_files/testing_tools/adapter/report.py @@ -13,7 +13,7 @@ def report_discovered( pretty=False, simple=False, _send=print, - **_ignored + **_ignored, ): """Serialize the discovered tests and write to stdout.""" if simple: diff --git a/python_files/testing_tools/adapter/util.py b/python_files/testing_tools/adapter/util.py index 4799980db53e..9f3089fb29d0 100644 --- a/python_files/testing_tools/adapter/util.py +++ b/python_files/testing_tools/adapter/util.py @@ -128,7 +128,7 @@ def fix_fileid( normalize=False, strictpathsep=None, _pathsep=PATH_SEP, - **kwargs + **kwargs, ): """Return a pathsep-separated file ID ("./"-prefixed) for the given value. @@ -150,7 +150,7 @@ def fix_fileid( rootdir, _pathsep=_pathsep, # ... - **kwargs + **kwargs, ) if relpath: # Note that we treat "" here as an absolute path. _fileid = "./" + relpath diff --git a/python_files/testing_tools/socket_manager.py b/python_files/testing_tools/socket_manager.py index b2afbf0e5a17..3392a4d54e07 100644 --- a/python_files/testing_tools/socket_manager.py +++ b/python_files/testing_tools/socket_manager.py @@ -29,9 +29,7 @@ def __exit__(self, *_): self.close() def connect(self): - self.socket = socket.socket( - socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP - ) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) if sys.platform == "win32": addr_use = socket.SO_EXCLUSIVEADDRUSE else: diff --git a/python_files/tests/__main__.py b/python_files/tests/__main__.py index 901385d41d87..347222bd85db 100644 --- a/python_files/tests/__main__.py +++ b/python_files/tests/__main__.py @@ -12,9 +12,7 @@ def parse_args(): parser = argparse.ArgumentParser() # To mark a test as functional: (decorator) @pytest.mark.functional - parser.add_argument( - "--functional", dest="markers", action="append_const", const="functional" - ) + parser.add_argument("--functional", dest="markers", action="append_const", const="functional") parser.add_argument( "--no-functional", dest="markers", action="append_const", const="not functional" ) diff --git a/python_files/tests/debug_adapter/test_install_debugpy.py b/python_files/tests/debug_adapter/test_install_debugpy.py index 45eff1272e00..f72e1089aaab 100644 --- a/python_files/tests/debug_adapter/test_install_debugpy.py +++ b/python_files/tests/debug_adapter/test_install_debugpy.py @@ -18,12 +18,8 @@ def test_install_debugpy(tmpdir): import install_debugpy install_debugpy.main(str(tmpdir)) - dir_path = os.path.join( - str(tmpdir), "debugpy", "_vendored", "pydevd", "_pydevd_bundle" - ) + dir_path = os.path.join(str(tmpdir), "debugpy", "_vendored", "pydevd", "_pydevd_bundle") _check_binaries(dir_path) - dir_path = os.path.join( - str(tmpdir), "debugpy", "_vendored", "pydevd", "_pydevd_frame_eval" - ) + dir_path = os.path.join(str(tmpdir), "debugpy", "_vendored", "pydevd", "_pydevd_frame_eval") _check_binaries(dir_path) diff --git a/python_files/tests/pytestadapter/expected_discovery_test_output.py b/python_files/tests/pytestadapter/expected_discovery_test_output.py index 7fbb0c5c43e7..ba42fcc68fe8 100644 --- a/python_files/tests/pytestadapter/expected_discovery_test_output.py +++ b/python_files/tests/pytestadapter/expected_discovery_test_output.py @@ -322,19 +322,12 @@ # └── test_bottom_function_t # └── test_bottom_function_f dual_level_nested_folder_path = TEST_DATA_PATH / "dual_level_nested_folder" -test_top_folder_path = ( - TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" -) +test_top_folder_path = TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" -test_nested_folder_one_path = ( - TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" -) +test_nested_folder_one_path = TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" test_bottom_folder_path = ( - TEST_DATA_PATH - / "dual_level_nested_folder" - / "nested_folder_one" - / "test_bottom_folder.py" + TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" / "test_bottom_folder.py" ) @@ -851,12 +844,8 @@ os.path.join(tests_path, "test_a.py"), ), "type_": "test", - "id_": get_absolute_test_id( - "tests/test_a.py::test_a_function", tests_a_path - ), - "runID": get_absolute_test_id( - "tests/test_a.py::test_a_function", tests_a_path - ), + "id_": get_absolute_test_id("tests/test_a.py::test_a_function", tests_a_path), + "runID": get_absolute_test_id("tests/test_a.py::test_a_function", tests_a_path), } ], }, @@ -874,12 +863,8 @@ os.path.join(tests_path, "test_b.py"), ), "type_": "test", - "id_": get_absolute_test_id( - "tests/test_b.py::test_b_function", tests_b_path - ), - "runID": get_absolute_test_id( - "tests/test_b.py::test_b_function", tests_b_path - ), + "id_": get_absolute_test_id("tests/test_b.py::test_b_function", tests_b_path), + "runID": get_absolute_test_id("tests/test_b.py::test_b_function", tests_b_path), } ], }, @@ -996,12 +981,8 @@ } SYMLINK_FOLDER_PATH = TEST_DATA_PATH / "symlink_folder" SYMLINK_FOLDER_PATH_TESTS = TEST_DATA_PATH / "symlink_folder" / "tests" -SYMLINK_FOLDER_PATH_TESTS_TEST_A = ( - TEST_DATA_PATH / "symlink_folder" / "tests" / "test_a.py" -) -SYMLINK_FOLDER_PATH_TESTS_TEST_B = ( - TEST_DATA_PATH / "symlink_folder" / "tests" / "test_b.py" -) +SYMLINK_FOLDER_PATH_TESTS_TEST_A = TEST_DATA_PATH / "symlink_folder" / "tests" / "test_a.py" +SYMLINK_FOLDER_PATH_TESTS_TEST_B = TEST_DATA_PATH / "symlink_folder" / "tests" / "test_b.py" symlink_expected_discovery_output = { "name": "symlink_folder", diff --git a/python_files/tests/pytestadapter/expected_execution_test_output.py b/python_files/tests/pytestadapter/expected_execution_test_output.py index db4e493c3daa..34693d36b125 100644 --- a/python_files/tests/pytestadapter/expected_execution_test_output.py +++ b/python_files/tests/pytestadapter/expected_execution_test_output.py @@ -20,9 +20,7 @@ test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" test_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" uf_execution_expected_output = { - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path ), @@ -31,9 +29,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path ), @@ -80,9 +76,7 @@ test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" uf_single_file_expected_output = { - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path ), @@ -91,9 +85,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path ), @@ -111,9 +103,7 @@ # │ └── TestAddFunction # │ └── test_add_positive_numbers: success uf_single_method_execution_expected_output = { - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path ), @@ -149,9 +139,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path - ): { + get_absolute_test_id(f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path): { "test": get_absolute_test_id( f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path ), @@ -252,35 +240,27 @@ "subtest": None, }, get_absolute_test_id("skip_tests.py::test_another_thing", skip_tests_path): { - "test": get_absolute_test_id( - "skip_tests.py::test_another_thing", skip_tests_path - ), + "test": get_absolute_test_id("skip_tests.py::test_another_thing", skip_tests_path), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, get_absolute_test_id("skip_tests.py::test_decorator_thing", skip_tests_path): { - "test": get_absolute_test_id( - "skip_tests.py::test_decorator_thing", skip_tests_path - ), + "test": get_absolute_test_id("skip_tests.py::test_decorator_thing", skip_tests_path), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, get_absolute_test_id("skip_tests.py::test_decorator_thing_2", skip_tests_path): { - "test": get_absolute_test_id( - "skip_tests.py::test_decorator_thing_2", skip_tests_path - ), + "test": get_absolute_test_id("skip_tests.py::test_decorator_thing_2", skip_tests_path), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, - get_absolute_test_id( - "skip_tests.py::TestClass::test_class_function_a", skip_tests_path - ): { + get_absolute_test_id("skip_tests.py::TestClass::test_class_function_a", skip_tests_path): { "test": get_absolute_test_id( "skip_tests.py::TestClass::test_class_function_a", skip_tests_path ), @@ -289,9 +269,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "skip_tests.py::TestClass::test_class_function_b", skip_tests_path - ): { + get_absolute_test_id("skip_tests.py::TestClass::test_class_function_b", skip_tests_path): { "test": get_absolute_test_id( "skip_tests.py::TestClass::test_class_function_b", skip_tests_path ), @@ -316,10 +294,7 @@ TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" ) dual_level_nested_folder_bottom_path = ( - TEST_DATA_PATH - / "dual_level_nested_folder" - / "nested_folder_one" - / "test_bottom_folder.py" + TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" / "test_bottom_folder.py" ) dual_level_nested_folder_execution_expected_output = { get_absolute_test_id( @@ -379,9 +354,7 @@ # └── test_nest.py # └── test_function: success -nested_folder_path = ( - TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" -) +nested_folder_path = TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" double_nested_folder_expected_execution_output = { get_absolute_test_id( "folder_a/folder_b/folder_a/test_nest.py::test_function", nested_folder_path @@ -403,9 +376,7 @@ parametrize_tests_path = TEST_DATA_PATH / "parametrize_tests.py" parametrize_tests_expected_execution_output = { - get_absolute_test_id( - "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path - ): { + get_absolute_test_id("parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path): { "test": get_absolute_test_id( "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path ), @@ -414,9 +385,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "parametrize_tests.py::test_adding[2+4-6]", parametrize_tests_path - ): { + get_absolute_test_id("parametrize_tests.py::test_adding[2+4-6]", parametrize_tests_path): { "test": get_absolute_test_id( "parametrize_tests.py::test_adding[2+4-6]", parametrize_tests_path ), @@ -425,9 +394,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "parametrize_tests.py::test_adding[6+9-16]", parametrize_tests_path - ): { + get_absolute_test_id("parametrize_tests.py::test_adding[6+9-16]", parametrize_tests_path): { "test": get_absolute_test_id( "parametrize_tests.py::test_adding[6+9-16]", parametrize_tests_path ), @@ -442,9 +409,7 @@ # └── parametrize_tests.py # └── test_adding[3+5-8]: success single_parametrize_tests_expected_execution_output = { - get_absolute_test_id( - "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path - ): { + get_absolute_test_id("parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path): { "test": get_absolute_test_id( "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path ), @@ -461,9 +426,7 @@ doc_test_path = TEST_DATA_PATH / "text_docstring.txt" doctest_pytest_expected_execution_output = { get_absolute_test_id("text_docstring.txt::text_docstring.txt", doc_test_path): { - "test": get_absolute_test_id( - "text_docstring.txt::text_docstring.txt", doc_test_path - ), + "test": get_absolute_test_id("text_docstring.txt::text_docstring.txt", doc_test_path), "outcome": "success", "message": None, "traceback": None, @@ -477,10 +440,7 @@ TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" ) dual_level_nested_folder_bottom_path = ( - TEST_DATA_PATH - / "dual_level_nested_folder" - / "nested_folder_one" - / "test_bottom_folder.py" + TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" / "test_bottom_folder.py" ) unittest_folder_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" unittest_folder_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" @@ -494,26 +454,20 @@ "subtest": None, }, get_absolute_test_id("test_top_function_t", dual_level_nested_folder_top_path): { - "test": get_absolute_test_id( - "test_top_function_t", dual_level_nested_folder_top_path - ), + "test": get_absolute_test_id("test_top_function_t", dual_level_nested_folder_top_path), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, get_absolute_test_id("test_top_function_f", dual_level_nested_folder_top_path): { - "test": get_absolute_test_id( - "test_top_function_f", dual_level_nested_folder_top_path - ), + "test": get_absolute_test_id("test_top_function_f", dual_level_nested_folder_top_path), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - get_absolute_test_id( - "test_bottom_function_t", dual_level_nested_folder_bottom_path - ): { + get_absolute_test_id("test_bottom_function_t", dual_level_nested_folder_bottom_path): { "test": get_absolute_test_id( "test_bottom_function_t", dual_level_nested_folder_bottom_path ), @@ -522,9 +476,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "test_bottom_function_f", dual_level_nested_folder_bottom_path - ): { + get_absolute_test_id("test_bottom_function_f", dual_level_nested_folder_bottom_path): { "test": get_absolute_test_id( "test_bottom_function_f", dual_level_nested_folder_bottom_path ), @@ -533,9 +485,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "TestAddFunction::test_add_negative_numbers", unittest_folder_add_path - ): { + get_absolute_test_id("TestAddFunction::test_add_negative_numbers", unittest_folder_add_path): { "test": get_absolute_test_id( "TestAddFunction::test_add_negative_numbers", unittest_folder_add_path ), @@ -544,9 +494,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "TestAddFunction::test_add_positive_numbers", unittest_folder_add_path - ): { + get_absolute_test_id("TestAddFunction::test_add_positive_numbers", unittest_folder_add_path): { "test": get_absolute_test_id( "TestAddFunction::test_add_positive_numbers", unittest_folder_add_path ), @@ -606,18 +554,14 @@ logging_test_expected_execution_output = { get_absolute_test_id("test_logging.py::test_logging2", test_logging_path): { - "test": get_absolute_test_id( - "test_logging.py::test_logging2", test_logging_path - ), + "test": get_absolute_test_id("test_logging.py::test_logging2", test_logging_path), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, get_absolute_test_id("test_logging.py::test_logging", test_logging_path): { - "test": get_absolute_test_id( - "test_logging.py::test_logging", test_logging_path - ), + "test": get_absolute_test_id("test_logging.py::test_logging", test_logging_path), "outcome": "success", "message": None, "traceback": None, @@ -632,9 +576,7 @@ test_safe_clear_env_vars_path = TEST_DATA_PATH / "test_env_vars.py" safe_clear_env_vars_expected_execution_output = { - get_absolute_test_id( - "test_env_vars.py::test_clear_env", test_safe_clear_env_vars_path - ): { + get_absolute_test_id("test_env_vars.py::test_clear_env", test_safe_clear_env_vars_path): { "test": get_absolute_test_id( "test_env_vars.py::test_clear_env", test_safe_clear_env_vars_path ), @@ -643,9 +585,7 @@ "traceback": None, "subtest": None, }, - get_absolute_test_id( - "test_env_vars.py::test_check_env", test_safe_clear_env_vars_path - ): { + get_absolute_test_id("test_env_vars.py::test_check_env", test_safe_clear_env_vars_path): { "test": get_absolute_test_id( "test_env_vars.py::test_check_env", test_safe_clear_env_vars_path ), diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index 8657da612954..dd69379a45b9 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -68,9 +68,7 @@ def create_server( def _new_sock() -> socket.socket: - sock: socket.socket = socket.socket( - socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP - ) + sock: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) options = [ ("SOL_SOCKET", "SO_KEEPALIVE", 1), ("IPPROTO_TCP", "TCP_KEEPIDLE", 1), @@ -88,9 +86,7 @@ def _new_sock() -> socket.socket: CONTENT_LENGTH: str = "Content-Length:" -Env_Dict = TypedDict( - "Env_Dict", {"TEST_UUID": str, "TEST_PORT": str, "PYTHONPATH": str} -) +Env_Dict = TypedDict("Env_Dict", {"TEST_UUID": str, "TEST_PORT": str, "PYTHONPATH": str}) def process_rpc_message(data: str) -> Tuple[Dict[str, Any], str]: @@ -133,9 +129,7 @@ def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]: return runner_with_cwd(args, TEST_DATA_PATH) -def runner_with_cwd( - args: List[str], path: pathlib.Path -) -> Optional[List[Dict[str, Any]]]: +def runner_with_cwd(args: List[str], path: pathlib.Path) -> Optional[List[Dict[str, Any]]]: """Run the pytest discovery and return the JSON data from the server.""" process_args: List[str] = [ sys.executable, @@ -177,9 +171,7 @@ def runner_with_cwd( return process_rpc_json(result[0]) if result else None -def _listen_on_socket( - listener: socket.socket, result: List[str], completed: threading.Event -): +def _listen_on_socket(listener: socket.socket, result: List[str], completed: threading.Event): """Listen on the socket for the JSON data from the server. Created as a separate function for clarity in threading. """ @@ -201,9 +193,7 @@ def _listen_on_socket( result.append("".join(all_data)) -def _run_test_code( - proc_args: List[str], proc_env, proc_cwd: str, completed: threading.Event -): +def _run_test_code(proc_args: List[str], proc_env, proc_cwd: str, completed: threading.Event): result = subprocess.run(proc_args, env=proc_env, cwd=proc_cwd) completed.set() return result diff --git a/python_files/tests/pytestadapter/test_discovery.py b/python_files/tests/pytestadapter/test_discovery.py index 42795fb7920f..942f741a4767 100644 --- a/python_files/tests/pytestadapter/test_discovery.py +++ b/python_files/tests/pytestadapter/test_discovery.py @@ -36,17 +36,13 @@ def test_import_error(tmp_path): temp_dir.mkdir() p = temp_dir / "error_pytest_import.py" shutil.copyfile(file_path, p) - actual: Optional[List[Dict[str, Any]]] = helpers.runner( - ["--collect-only", os.fspath(p)] - ) + actual: Optional[List[Dict[str, Any]]] = helpers.runner(["--collect-only", os.fspath(p)]) assert actual actual_list: List[Dict[str, Any]] = actual if actual_list is not None: assert actual_list.pop(-1).get("eot") for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "error") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "error" assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) @@ -89,9 +85,7 @@ def test_syntax_error(tmp_path): if actual_list is not None: assert actual_list.pop(-1).get("eot") for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "error") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "error" assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) @@ -117,9 +111,7 @@ def test_parameterized_error_collect(): if actual_list is not None: assert actual_list.pop(-1).get("eot") for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "error") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "error" assert actual_item.get("cwd") == os.fspath(helpers.TEST_DATA_PATH) @@ -243,9 +235,7 @@ def test_symlink_root_dir(): assert actual_item.get("cwd") == os.fspath( destination ), f"CWD does not match: {os.fspath(destination)}" - assert ( - actual_item.get("tests") == expected - ), "Tests do not match expected value" + assert actual_item.get("tests") == expected, "Tests do not match expected value" except AssertionError as e: # Print the actual_item in JSON format if an assertion fails print(json.dumps(actual_item, indent=4)) diff --git a/python_files/tests/pytestadapter/test_execution.py b/python_files/tests/pytestadapter/test_execution.py index a8336089d0a9..b4fffd6a640b 100644 --- a/python_files/tests/pytestadapter/test_execution.py +++ b/python_files/tests/pytestadapter/test_execution.py @@ -28,9 +28,7 @@ def test_config_file(): ] new_cwd = TEST_DATA_PATH / "root" actual = runner_with_cwd(args, new_cwd) - expected_const = ( - expected_execution_test_output.config_file_pytest_expected_execution_output - ) + expected_const = expected_execution_test_output.config_file_pytest_expected_execution_output assert actual actual_list: List[Dict[str, Any]] = actual assert actual_list.pop(-1).get("eot") @@ -38,9 +36,7 @@ def test_config_file(): actual_result_dict = dict() if actual_list is not None: for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "result") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "result")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(new_cwd) actual_result_dict.update(actual_item["result"]) @@ -53,9 +49,7 @@ def test_rootdir_specified(): args = [rd, "tests/test_a.py::test_a_function"] new_cwd = TEST_DATA_PATH / "root" actual = runner_with_cwd(args, new_cwd) - expected_const = ( - expected_execution_test_output.config_file_pytest_expected_execution_output - ) + expected_const = expected_execution_test_output.config_file_pytest_expected_execution_output assert actual actual_list: List[Dict[str, Any]] = actual assert actual_list.pop(-1).get("eot") @@ -63,9 +57,7 @@ def test_rootdir_specified(): actual_result_dict = dict() if actual_list is not None: for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "result") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "result")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(new_cwd) actual_result_dict.update(actual_item["result"]) @@ -101,9 +93,7 @@ def test_syntax_error_execution(tmp_path): assert actual_list.pop(-1).get("eot") if actual_list is not None: for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "error") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "error" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) error_content = actual_item.get("error") @@ -126,9 +116,7 @@ def test_bad_id_error_execution(): assert actual_list.pop(-1).get("eot") if actual_list is not None: for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "error") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "error")) assert actual_item.get("status") == "error" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) error_content = actual_item.get("error") @@ -273,9 +261,7 @@ def test_pytest_execution(test_ids, expected_const): actual_result_dict = dict() if actual_list is not None: for actual_item in actual_list: - assert all( - item in actual_item.keys() for item in ("status", "cwd", "result") - ) + assert all(item in actual_item.keys() for item in ("status", "cwd", "result")) assert actual_item.get("status") == "success" assert actual_item.get("cwd") == os.fspath(TEST_DATA_PATH) actual_result_dict.update(actual_item["result"]) @@ -307,13 +293,9 @@ def test_symlink_run(): ) # Run pytest with the cwd being the resolved symlink path (as it will be when we run the subprocess from node). - actual = runner_with_cwd( - [f"--rootdir={os.fspath(destination)}", test_a_id], source - ) + actual = runner_with_cwd([f"--rootdir={os.fspath(destination)}", test_a_id], source) - expected_const = ( - expected_execution_test_output.symlink_run_expected_execution_output - ) + expected_const = expected_execution_test_output.symlink_run_expected_execution_output assert actual actual_list: List[Dict[str, Any]] = actual if actual_list is not None: diff --git a/python_files/tests/test_create_conda.py b/python_files/tests/test_create_conda.py index 29dc323402eb..8681184ba821 100644 --- a/python_files/tests/test_create_conda.py +++ b/python_files/tests/test_create_conda.py @@ -29,9 +29,7 @@ def install_packages(_name): def run_process(args, error_message): nonlocal run_process_called run_process_called = True - version = ( - "12345" if python else f"{sys.version_info.major}.{sys.version_info.minor}" - ) + version = "12345" if python else f"{sys.version_info.major}.{sys.version_info.minor}" if not env_exists: assert args == [ sys.executable, diff --git a/python_files/tests/test_create_venv.py b/python_files/tests/test_create_venv.py index 446d6da8bc55..1539f1d9b44e 100644 --- a/python_files/tests/test_create_venv.py +++ b/python_files/tests/test_create_venv.py @@ -14,9 +14,7 @@ import create_venv -@pytest.mark.skipif( - sys.platform == "win32", reason="Windows does not have micro venv fallback." -) +@pytest.mark.skipif(sys.platform == "win32", reason="Windows does not have micro venv fallback.") def test_venv_not_installed_unix(): importlib.reload(create_venv) create_venv.is_installed = lambda module: module != "venv" @@ -43,9 +41,7 @@ def run_process(args, error_message): assert run_process_called is True -@pytest.mark.skipif( - sys.platform != "win32", reason="Windows does not have microvenv fallback." -) +@pytest.mark.skipif(sys.platform != "win32", reason="Windows does not have microvenv fallback.") def test_venv_not_installed_windows(): importlib.reload(create_venv) create_venv.is_installed = lambda module: module != "venv" @@ -106,9 +102,7 @@ def add_gitignore(_name): assert run_process_called == (env_exists == "noEnv") # add_gitignore is called when new venv is created and git_ignore is True - assert add_gitignore_called == ( - (env_exists == "noEnv") and (git_ignore == "useGitIgnore") - ) + assert add_gitignore_called == ((env_exists == "noEnv") and (git_ignore == "useGitIgnore")) @pytest.mark.parametrize("install_type", ["requirements", "pyproject", "both"]) @@ -238,9 +232,7 @@ def run_process(args, error_message): if "install" in args and "pip" in args: nonlocal run_process_called run_process_called = True - pip_pyz_path = os.fspath( - create_venv.CWD / create_venv.VENV_NAME / "pip.pyz" - ) + pip_pyz_path = os.fspath(create_venv.CWD / create_venv.VENV_NAME / "pip.pyz") assert args[1:] == [pip_pyz_path, "install", "pip"] assert error_message == "CREATE_VENV.INSTALL_PIP_FAILED" diff --git a/python_files/tests/testing_tools/adapter/pytest/test_discovery.py b/python_files/tests/testing_tools/adapter/pytest/test_discovery.py index 83eeaa1f9062..55a0e65102ae 100644 --- a/python_files/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/python_files/tests/testing_tools/adapter/pytest/test_discovery.py @@ -224,7 +224,7 @@ def _fix_fileid(*args): # dependency injection _normcase=normcase, _pathsep=pathsep, - ) + ), ) def _normalize_test_id(*args): @@ -234,7 +234,7 @@ def _normalize_test_id(*args): # dependency injection _fix_fileid=_fix_fileid, _pathsep=pathsep, - ) + ), ) def _iter_nodes(*args): @@ -245,7 +245,7 @@ def _iter_nodes(*args): _normalize_test_id=_normalize_test_id, _normcase=normcase, _pathsep=pathsep, - ) + ), ) def _parse_node_id(*args): @@ -254,7 +254,7 @@ def _parse_node_id(*args): **dict( # dependency injection _iter_nodes=_iter_nodes, - ) + ), ) ########## @@ -264,7 +264,7 @@ def _split_fspath(*args): **dict( # dependency injection _normcase=normcase, - ) + ), ) ########## @@ -275,7 +275,7 @@ def _matches_relfile(*args): # dependency injection _normcase=normcase, _pathsep=pathsep, - ) + ), ) def _is_legacy_wrapper(*args): @@ -284,7 +284,7 @@ def _is_legacy_wrapper(*args): **dict( # dependency injection _pathsep=pathsep, - ) + ), ) def _get_location(*args): @@ -295,7 +295,7 @@ def _get_location(*args): _matches_relfile=_matches_relfile, _is_legacy_wrapper=_is_legacy_wrapper, _pathsep=pathsep, - ) + ), ) ########## @@ -307,7 +307,7 @@ def _parse_item(item): _parse_node_id=_parse_node_id, _split_fspath=_split_fspath, _get_location=_get_location, - ) + ), ) return _parse_item @@ -347,9 +347,7 @@ def test_basic(self): ("discovered.__getitem__", (0,), None), ] - parents, tests = _discovery.discover( - [], _pytest_main=stubpytest.main, _plugin=plugin - ) + parents, tests = _discovery.discover([], _pytest_main=stubpytest.main, _plugin=plugin) actual_calls = unique(stub.calls, lambda k: k[0]) expected_calls = unique(calls, lambda k: k[0]) @@ -389,9 +387,7 @@ def test_no_tests_found(self): ("discovered.__getitem__", (0,), None), ] - parents, tests = _discovery.discover( - [], _pytest_main=pytest.main, _plugin=plugin - ) + parents, tests = _discovery.discover([], _pytest_main=pytest.main, _plugin=plugin) actual_calls = unique(stub.calls, lambda k: k[0]) expected_calls = unique(calls, lambda k: k[0]) @@ -414,9 +410,7 @@ def test_found_with_collection_error(self): ("discovered.__getitem__", (0,), None), ] - parents, tests = _discovery.discover( - [], _pytest_main=pytest.main, _plugin=plugin - ) + parents, tests = _discovery.discover([], _pytest_main=pytest.main, _plugin=plugin) actual_calls = unique(stub.calls, lambda k: k[0]) expected_calls = unique(calls, lambda k: k[0]) @@ -845,9 +839,7 @@ def test_finish(self): func="SpamTests.test_spam", sub=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests", ), @@ -920,9 +912,7 @@ def test_doctest(self): relfile=adapter_util.fix_relpath(doctestfile), func=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(doctestfile), 1 - ), + source="{}:{}".format(adapter_util.fix_relpath(doctestfile), 1), markers=[], parentid="./x/test_doctest.txt", ), @@ -972,9 +962,7 @@ def test_doctest(self): relfile=adapter_util.fix_relpath(relfile), func=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=[], parentid="./x/y/z/test_eggs.py", ), @@ -999,9 +987,7 @@ def test_doctest(self): relfile=adapter_util.fix_relpath(relfile), func=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 28 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 28), markers=[], parentid="./x/y/z/test_eggs.py", ), @@ -1062,9 +1048,7 @@ def test_nested_brackets(self): func="SpamTests.test_spam", sub=["[a-[b]-c]"], ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests::test_spam", ), @@ -1126,9 +1110,7 @@ def test_nested_suite(self): func="SpamTests.Ham.Eggs.test_spam", sub=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests::Ham::Eggs", ), @@ -1479,9 +1461,7 @@ def test_mysterious_parens(self): func="SpamTests.test_spam", sub=[], ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests", ), @@ -1537,9 +1517,7 @@ def test_mysterious_colons(self): func="SpamTests.test_spam", sub=[], ), - source="{}:{}".format( - adapter_util.fix_relpath(relfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(relfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests", ), @@ -1607,9 +1585,7 @@ def test_imported_test(self): func="SpamTests.test_spam", sub=None, ), - source="{}:{}".format( - adapter_util.fix_relpath(srcfile), 13 - ), + source="{}:{}".format(adapter_util.fix_relpath(srcfile), 13), markers=None, parentid="./x/y/z/test_eggs.py::SpamTests", ), diff --git a/python_files/tests/testing_tools/adapter/test_discovery.py b/python_files/tests/testing_tools/adapter/test_discovery.py index c01e625becde..2fe4db7caa37 100644 --- a/python_files/tests/testing_tools/adapter/test_discovery.py +++ b/python_files/tests/testing_tools/adapter/test_discovery.py @@ -252,9 +252,7 @@ def test_add_test_simple(self): # missing "./": parentid=relfile, ) - expected = test._replace( - id=_fix_nodeid(test.id), parentid=_fix_nodeid(test.parentid) - ) + expected = test._replace(id=_fix_nodeid(test.id), parentid=_fix_nodeid(test.parentid)) discovered = DiscoveredTests() before = list(discovered), discovered.parents diff --git a/python_files/tests/testing_tools/adapter/test_functional.py b/python_files/tests/testing_tools/adapter/test_functional.py index a78d36a5fdcf..45c85ee951dc 100644 --- a/python_files/tests/testing_tools/adapter/test_functional.py +++ b/python_files/tests/testing_tools/adapter/test_functional.py @@ -48,9 +48,7 @@ def _run_adapter(cmd, tool, *cliargs, **kwargs): argv.insert(4, "--no-hide-stdio") kwds["stderr"] = subprocess.STDOUT argv.append("--cache-clear") - print( - "running {!r}".format(" ".join(arg.rpartition(CWD + "/")[-1] for arg in argv)) - ) + print("running {!r}".format(" ".join(arg.rpartition(CWD + "/")[-1] for arg in argv))) output = subprocess.check_output(argv, universal_newlines=True, **kwds) return output diff --git a/python_files/tests/testing_tools/adapter/test_util.py b/python_files/tests/testing_tools/adapter/test_util.py index 3fcafd1a93d5..8a7cd475a1c7 100644 --- a/python_files/tests/testing_tools/adapter/test_util.py +++ b/python_files/tests/testing_tools/adapter/test_util.py @@ -259,9 +259,7 @@ def test_fix_fileid(self): ) for fileid, rootdir, _os_path, expected in tests: pathsep = _os_path.sep - with self.subTest( - r"for {} (with rootdir {!r}): {!r}".format(pathsep, rootdir, fileid) - ): + with self.subTest(r"for {} (with rootdir {!r}): {!r}".format(pathsep, rootdir, fileid)): fixed = fix_fileid( fileid, rootdir, diff --git a/python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py b/python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py index 4f3f77e1056e..52641360b526 100644 --- a/python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py +++ b/python_files/tests/unittestadapter/.data/two_patterns/pattern_a_test.py @@ -7,7 +7,6 @@ # and the two tests with their outcome as "success". - class DiscoveryA(unittest.TestCase): """Test class for the two file pattern test. It is pattern *test.py @@ -19,4 +18,4 @@ def test_one_a(self) -> None: self.assertGreater(2, 1) def test_two_a(self) -> None: - self.assertNotEqual(2, 1) \ No newline at end of file + self.assertNotEqual(2, 1) diff --git a/python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py b/python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py index a912699383ca..06b6a818537d 100644 --- a/python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py +++ b/python_files/tests/unittestadapter/.data/two_patterns/test_pattern_b.py @@ -6,10 +6,10 @@ # The test_ids_multiple_runs function should return a dictionary with a "success" status, # and the two tests with their outcome as "success". -class DiscoveryB(unittest.TestCase): +class DiscoveryB(unittest.TestCase): def test_one_b(self) -> None: self.assertGreater(2, 1) def test_two_b(self) -> None: - self.assertNotEqual(2, 1) \ No newline at end of file + self.assertNotEqual(2, 1) diff --git a/python_files/tests/unittestadapter/.data/unittest_folder/test_add.py b/python_files/tests/unittestadapter/.data/unittest_folder/test_add.py index 2e616077ec40..f562474b596a 100644 --- a/python_files/tests/unittestadapter/.data/unittest_folder/test_add.py +++ b/python_files/tests/unittestadapter/.data/unittest_folder/test_add.py @@ -6,17 +6,16 @@ # files in the same folder. The cwd is set to the parent folder. This should return # a dictionary with a "success" status and the two tests with their outcome as "success". + def add(a, b): return a + b class TestAddFunction(unittest.TestCase): - - def test_add_positive_numbers(self): + def test_add_positive_numbers(self): result = add(2, 3) self.assertEqual(result, 5) - - def test_add_negative_numbers(self): + def test_add_negative_numbers(self): result = add(-2, -3) - self.assertEqual(result, -5) \ No newline at end of file + self.assertEqual(result, -5) diff --git a/python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py b/python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py index 4028e25825d1..8ac3988a3251 100644 --- a/python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py +++ b/python_files/tests/unittestadapter/.data/unittest_folder/test_subtract.py @@ -6,6 +6,7 @@ # files in the same folder. The cwd is set to the parent folder. This should return # a dictionary with a "success" status and the two tests with their outcome as "success". + def subtract(a, b): return a - b @@ -15,7 +16,6 @@ def test_subtract_positive_numbers(self): result = subtract(5, 3) self.assertEqual(result, 2) - def test_subtract_negative_numbers(self): result = subtract(-2, -3) - self.assertEqual(result, 1) \ No newline at end of file + self.assertEqual(result, 1) diff --git a/python_files/tests/unittestadapter/expected_discovery_test_output.py b/python_files/tests/unittestadapter/expected_discovery_test_output.py index 1007a8f42dfd..9fca67a3a574 100644 --- a/python_files/tests/unittestadapter/expected_discovery_test_output.py +++ b/python_files/tests/unittestadapter/expected_discovery_test_output.py @@ -14,20 +14,14 @@ "type_": TestNodeTypeEnum.folder, "children": [ { - "path": os.fspath( - TEST_DATA_PATH / "unittest_skip" / "unittest_skip_file.py" - ), + "path": os.fspath(TEST_DATA_PATH / "unittest_skip" / "unittest_skip_file.py"), "name": "unittest_skip_file.py", "type_": TestNodeTypeEnum.file, "children": [], - "id_": os.fspath( - TEST_DATA_PATH / "unittest_skip" / "unittest_skip_file.py" - ), + "id_": os.fspath(TEST_DATA_PATH / "unittest_skip" / "unittest_skip_file.py"), }, { - "path": os.fspath( - TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py" - ), + "path": os.fspath(TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py"), "name": "unittest_skip_function.py", "type_": TestNodeTypeEnum.file, "children": [ @@ -41,30 +35,22 @@ { "name": "testadd1", "path": os.fspath( - TEST_DATA_PATH - / "unittest_skip" - / "unittest_skip_function.py" + TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py" ), "lineno": "13", "type_": TestNodeTypeEnum.test, "id_": os.fspath( - TEST_DATA_PATH - / "unittest_skip" - / "unittest_skip_function.py" + TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py" ) + "\\SimpleTest\\testadd1", "runID": "unittest_skip_function.SimpleTest.testadd1", } ], - "id_": os.fspath( - TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py" - ) + "id_": os.fspath(TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py") + "\\SimpleTest", } ], - "id_": os.fspath( - TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py" - ), + "id_": os.fspath(TEST_DATA_PATH / "unittest_skip" / "unittest_skip_function.py"), }, ], "id_": os.fspath(TEST_DATA_PATH / "unittest_skip"), @@ -88,9 +74,7 @@ "name": "test_outer_folder", "type_": TestNodeTypeEnum.folder, "path": os.fsdecode( - pathlib.PurePath( - TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder" - ) + pathlib.PurePath(TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder") ), "children": [ { @@ -145,9 +129,7 @@ }, ], "id_": os.fsdecode( - pathlib.PurePath( - TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder" - ) + pathlib.PurePath(TEST_DATA_PATH, "utils_complex_tree", "test_outer_folder") ), } ], diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index fadc2aa3f7e2..462b9cf9b0fe 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -107,22 +107,14 @@ def test_simple_discovery() -> None: "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "14", - "id_": file_path - + "\\" - + "DiscoverySimple" - + "\\" - + "test_one", + "id_": file_path + "\\" + "DiscoverySimple" + "\\" + "test_one", }, { "name": "test_two", "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "17", - "id_": file_path - + "\\" - + "DiscoverySimple" - + "\\" - + "test_two", + "id_": file_path + "\\" + "DiscoverySimple" + "\\" + "test_two", }, ], "id_": file_path + "\\" + "DiscoverySimple", @@ -170,22 +162,14 @@ def test_simple_discovery_with_top_dir_calculated() -> None: "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "14", - "id_": file_path - + "\\" - + "DiscoverySimple" - + "\\" - + "test_one", + "id_": file_path + "\\" + "DiscoverySimple" + "\\" + "test_one", }, { "name": "test_two", "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "17", - "id_": file_path - + "\\" - + "DiscoverySimple" - + "\\" - + "test_two", + "id_": file_path + "\\" + "DiscoverySimple" + "\\" + "test_two", }, ], "id_": file_path + "\\" + "DiscoverySimple", @@ -253,22 +237,14 @@ def test_error_discovery() -> None: "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "14", - "id_": file_path - + "\\" - + "DiscoveryErrorTwo" - + "\\" - + "test_one", + "id_": file_path + "\\" + "DiscoveryErrorTwo" + "\\" + "test_one", }, { "name": "test_two", "path": file_path, "type_": TestNodeTypeEnum.test, "lineno": "17", - "id_": file_path - + "\\" - + "DiscoveryErrorTwo" - + "\\" - + "test_two", + "id_": file_path + "\\" + "DiscoveryErrorTwo" + "\\" + "test_two", }, ], "id_": file_path + "\\" + "DiscoveryErrorTwo", diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index a8dc8ed38097..298fe027d1d9 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -98,9 +98,7 @@ def discover_tests( # Get abspath of top level directory for build_test_tree. top_level_dir = os.path.abspath(top_level_dir) - tests, error = build_test_tree( - suite, top_level_dir - ) # test tree built successfully here. + tests, error = build_test_tree(suite, top_level_dir) # test tree built successfully here. except Exception: error.append(traceback.format_exc()) @@ -116,9 +114,7 @@ def discover_tests( return payload -def post_response( - payload: Union[PayloadDict, EOTPayloadDict], port: int, uuid: str -) -> None: +def post_response(payload: Union[PayloadDict, EOTPayloadDict], port: int, uuid: str) -> None: # Build the request data (it has to be a POST request or the Node side will not process it), and send it. addr = ("localhost", port) data = json.dumps(payload) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index f433c117ec7c..9394303b69a1 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -22,9 +22,7 @@ from testing_tools import process_json_util, socket_manager # noqa: E402 from unittestadapter.pvsc_utils import parse_unittest_args # noqa: E402 -ErrorType = Union[ - Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None] -] +ErrorType = Union[Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None]] testPort = 0 testUuid = 0 START_DIR = "" @@ -244,9 +242,7 @@ def send_run_data(raw_data, port, uuid): post_response(payload, port, uuid) -def post_response( - payload: Union[PayloadDict, EOTPayloadDict], port: int, uuid: str -) -> None: +def post_response(payload: Union[PayloadDict, EOTPayloadDict], port: int, uuid: str) -> None: # Build the request data (it has to be a POST request or the Node side will not process it), and send it. addr = ("localhost", port) global __socket @@ -286,9 +282,7 @@ def post_response( ) = parse_unittest_args(argv[index + 1 :]) run_test_ids_port = os.environ.get("RUN_TEST_IDS_PORT") - run_test_ids_port_int = ( - int(run_test_ids_port) if run_test_ids_port is not None else 0 - ) + run_test_ids_port_int = int(run_test_ids_port) if run_test_ids_port is not None else 0 if run_test_ids_port_int == 0: print("Error[vscode-unittest]: RUN_TEST_IDS_PORT env var is not set.") # get data from socket @@ -309,9 +303,7 @@ def post_response( try: # Try to parse the buffer as JSON - test_ids_from_buffer = process_json_util.process_rpc_json( - buffer.decode("utf-8") - ) + test_ids_from_buffer = process_json_util.process_rpc_json(buffer.decode("utf-8")) # Clear the buffer as complete JSON object is received buffer = b"" break diff --git a/python_files/unittestadapter/pvsc_utils.py b/python_files/unittestadapter/pvsc_utils.py index 673da2aa1eec..de4f23957b8b 100644 --- a/python_files/unittestadapter/pvsc_utils.py +++ b/python_files/unittestadapter/pvsc_utils.py @@ -91,9 +91,7 @@ def build_test_node(path: str, name: str, type_: TestNodeTypeEnum) -> TestNode: return {"path": path, "name": name, "type_": type_, "children": [], "id_": id_gen} -def get_child_node( - name: str, path: str, type_: TestNodeTypeEnum, root: TestNode -) -> TestNode: +def get_child_node(name: str, path: str, type_: TestNodeTypeEnum, root: TestNode) -> TestNode: """Find a child node in a test tree given its name, type and path. If the node doesn't exist, create it. Path is required to distinguish between nodes with the same name and type.""" try: @@ -154,9 +152,7 @@ def build_test_tree( """ error = [] directory_path = pathlib.PurePath(top_level_directory) - root = build_test_node( - top_level_directory, directory_path.name, TestNodeTypeEnum.folder - ) + root = build_test_node(top_level_directory, directory_path.name, TestNodeTypeEnum.folder) for test_case in get_test_case(suite): test_id = test_case.id() @@ -167,9 +163,7 @@ def build_test_tree( class_name = f"{components[-1]}.py" # Find/build class node. file_path = os.fsdecode(os.path.join(directory_path, class_name)) - current_node = get_child_node( - class_name, file_path, TestNodeTypeEnum.file, root - ) + current_node = get_child_node(class_name, file_path, TestNodeTypeEnum.file, root) else: # Get the static test path components: filename, class name and function name. components = test_id.split(".") diff --git a/python_files/visualstudio_py_testlauncher.py b/python_files/visualstudio_py_testlauncher.py index 854a9f4fae5a..b085d5ce4e6f 100644 --- a/python_files/visualstudio_py_testlauncher.py +++ b/python_files/visualstudio_py_testlauncher.py @@ -170,9 +170,7 @@ def addUnexpectedSuccess(self, test): def addSubTest(self, test, subtest, err): super(VsTestResult, self).addSubTest(test, subtest, err) - self.sendResult( - test, "subtest-passed" if err is None else "subtest-failed", err, subtest - ) + self.sendResult(test, "subtest-passed" if err is None else "subtest-failed", err, subtest) def sendResult(self, test, outcome, trace=None, subtest=None): if _channel is not None: @@ -224,9 +222,7 @@ def main(): prog="visualstudio_py_testlauncher", usage="Usage: %prog [