Skip to content

Run extension-internal scripts "isolated". #10941

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions pythonFiles/pyvsc-run-isolated.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
del sys.path[0]
del sys.argv[0]
module = sys.argv[0]
if module.endswith(".py"):
runpy.run_path(module)
if module.startswith("-"):
raise NotImplementedError(sys.argv)
elif module.endswith(".py"):
runpy.run_path(module, run_name="__main__")
else:
runpy.run_module(module, alter_sys=True)
runpy.run_module(module, run_name="__main__", alter_sys=True)
23 changes: 13 additions & 10 deletions src/client/common/process/internal/scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { PythonVersionInfo } from '../../types';
// It is simpler to hard-code it instead of using vscode.ExtensionContext.extensionPath.
export const _SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, 'pythonFiles');
const SCRIPTS_DIR = _SCRIPTS_DIR;
export const _ISOLATED = path.join(_SCRIPTS_DIR, 'pyvsc-run-isolated.py');
const ISOLATED = _ISOLATED;

// "scripts" contains everything relevant to the scripts found under
// the top-level "pythonFiles" directory. Each of those scripts has
Expand Down Expand Up @@ -48,7 +50,7 @@ type PythonEnvInfo = {

export function interpreterInfo(): [string[], (out: string) => PythonEnvInfo] {
const script = path.join(SCRIPTS_DIR, 'interpreterInfo.py');
const args = [script];
const args = [ISOLATED, script];

function parse(out: string): PythonEnvInfo {
let json: PythonEnvInfo;
Expand Down Expand Up @@ -158,7 +160,7 @@ namespace _completion {

export function completion(jediPath?: string): [string[], (out: string) => _completion.Response[]] {
const script = path.join(SCRIPTS_DIR, 'completion.py');
const args = [script];
const args = [ISOLATED, script];
if (jediPath) {
args.push('custom');
args.push(jediPath);
Expand All @@ -176,7 +178,7 @@ export function completion(jediPath?: string): [string[], (out: string) => _comp

export function sortImports(filename: string, sortArgs?: string[]): [string[], (out: string) => string] {
const script = path.join(SCRIPTS_DIR, 'sortImports.py');
const args = [script, filename, '--diff'];
const args = [ISOLATED, script, filename, '--diff'];
if (sortArgs) {
args.push(...sortArgs);
}
Expand All @@ -194,7 +196,7 @@ export function sortImports(filename: string, sortArgs?: string[]): [string[], (

export function refactor(root: string): [string[], (out: string) => object[]] {
const script = path.join(SCRIPTS_DIR, 'refactor.py');
const args = [script, root];
const args = [ISOLATED, script, root];

// tslint:disable-next-line:no-suspicious-comment
// TODO: Make the return type more specific, like we did
Expand All @@ -216,7 +218,7 @@ export function refactor(root: string): [string[], (out: string) => object[]] {

export function normalizeForInterpreter(code: string): [string[], (out: string) => string] {
const script = path.join(SCRIPTS_DIR, 'normalizeForInterpreter.py');
const args = [script, code];
const args = [ISOLATED, script, code];

function parse(out: string) {
// The text will be used as-is.
Expand Down Expand Up @@ -256,7 +258,7 @@ export function symbolProvider(
text?: string
): [string[], (out: string) => _symbolProvider.Symbols] {
const script = path.join(SCRIPTS_DIR, 'symbolProvider.py');
const args = [script, filename];
const args = [ISOLATED, script, filename];
if (text) {
args.push(text);
}
Expand All @@ -273,7 +275,7 @@ export function symbolProvider(

export function printEnvVariables(): [string[], (out: string) => NodeJS.ProcessEnv] {
const script = path.join(SCRIPTS_DIR, 'printEnvVariables.py').fileToCommandArgument();
const args = [script];
const args = [ISOLATED, script];

function parse(out: string): NodeJS.ProcessEnv {
return JSON.parse(out);
Expand All @@ -287,7 +289,7 @@ export function printEnvVariables(): [string[], (out: string) => NodeJS.ProcessE

export function printEnvVariablesToFile(filename: string): [string[], (out: string) => NodeJS.ProcessEnv] {
const script = path.join(SCRIPTS_DIR, 'printEnvVariablesToFile.py');
const args = [script, filename.fileToCommandArgument()];
const args = [ISOLATED, script, filename.fileToCommandArgument()];

function parse(out: string): NodeJS.ProcessEnv {
return JSON.parse(out);
Expand All @@ -304,6 +306,7 @@ export function shell_exec(command: string, lockfile: string, shellArgs: string[
// We don't bother with a "parse" function since the output
// could be anything.
return [
ISOLATED,
script,
command.fileToCommandArgument(),
// The shell args must come after the command
Expand All @@ -319,7 +322,7 @@ export function shell_exec(command: string, lockfile: string, shellArgs: string[
export function testlauncher(testArgs: string[]): string[] {
const script = path.join(SCRIPTS_DIR, 'testlauncher.py');
// There is no output to parse, so we do not return a function.
return [script, ...testArgs];
return [ISOLATED, script, ...testArgs];
}

//============================
Expand All @@ -328,5 +331,5 @@ export function testlauncher(testArgs: string[]): string[] {
export function visualstudio_py_testlauncher(testArgs: string[]): string[] {
const script = path.join(SCRIPTS_DIR, 'visualstudio_py_testlauncher.py');
// There is no output to parse, so we do not return a function.
return [script, ...testArgs];
return [ISOLATED, script, ...testArgs];
}
3 changes: 3 additions & 0 deletions src/client/common/process/internal/scripts/testing_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export type DiscoveredTests = {

export function run_adapter(adapterArgs: string[]): [string[], (out: string) => DiscoveredTests[]] {
const script = path.join(SCRIPTS_DIR, 'run_adapter.py');
// Note that we for now we do not run this "isolated". The
// script relies on some magic that conflicts with the
// isolated script.
const args = [script, ...adapterArgs];

function parse(out: string): DiscoveredTests[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

import * as path from 'path';
import { _SCRIPTS_DIR } from './index';
import { _ISOLATED as ISOLATED, _SCRIPTS_DIR } from './index';

const SCRIPTS_DIR = path.join(_SCRIPTS_DIR, 'vscode_datascience_helpers');

Expand All @@ -12,7 +12,7 @@ const SCRIPTS_DIR = path.join(_SCRIPTS_DIR, 'vscode_datascience_helpers');
export function getJupyterVariableDataFrameInfo(): string[] {
const script = path.join(SCRIPTS_DIR, 'getJupyterVariableDataFrameInfo.py');
// There is no script-specific output to parse, so we do not return a function.
return [script];
return [ISOLATED, script];
}

//============================
Expand All @@ -21,7 +21,7 @@ export function getJupyterVariableDataFrameInfo(): string[] {
export function getJupyterVariableDataFrameRows(): string[] {
const script = path.join(SCRIPTS_DIR, 'getJupyterVariableDataFrameRows.py');
// There is no script-specific output to parse, so we do not return a function.
return [script];
return [ISOLATED, script];
}

//============================
Expand All @@ -41,7 +41,7 @@ type JupyterServerInfo = {

export function getServerInfo(): [string[], (out: string) => JupyterServerInfo[]] {
const script = path.join(SCRIPTS_DIR, 'getServerInfo.py');
const args = [script];
const args = [ISOLATED, script];

function parse(out: string): JupyterServerInfo[] {
return JSON.parse(out.trim());
Expand All @@ -56,7 +56,7 @@ export function getServerInfo(): [string[], (out: string) => JupyterServerInfo[]
export function getJupyterKernels(): string[] {
const script = path.join(SCRIPTS_DIR, 'getJupyterKernels.py');
// There is no script-specific output to parse, so we do not return a function.
return [script];
return [ISOLATED, script];
}

//============================
Expand All @@ -65,15 +65,15 @@ export function getJupyterKernels(): string[] {
export function getJupyterKernelspecVersion(): string[] {
const script = path.join(SCRIPTS_DIR, 'getJupyterKernelspecVersion.py');
// For now we do not worry about parsing the output here.
return [script];
return [ISOLATED, script];
}

//============================
// jupyter_nbInstalled.py

export function jupyter_nbInstalled(): [string[], (out: string) => boolean] {
const script = path.join(SCRIPTS_DIR, 'jupyter_nbInstalled.py');
const args = [script];
const args = [ISOLATED, script];

function parse(out: string): boolean {
return out.toLowerCase().includes('available');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ suite('Terminal Service (synchronous)', () => {
});
});
suite('sendCommand', () => {
const isolated = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py');
const shellExecFile = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'shell_exec.py');

test('run sendCommand in terminalService if there is no cancellation token', async () => {
Expand Down Expand Up @@ -107,6 +108,7 @@ suite('Terminal Service (synchronous)', () => {
terminalService.sendCommand(
'python',
deepEqual([
isolated.fileToCommandArgument(),
shellExecFile.fileToCommandArgument(),
'cmd'.fileToCommandArgument(),
'1',
Expand Down Expand Up @@ -149,6 +151,7 @@ suite('Terminal Service (synchronous)', () => {
terminalService.sendCommand(
'python',
deepEqual([
isolated.fileToCommandArgument(),
shellExecFile.fileToCommandArgument(),
'cmd'.fileToCommandArgument(),
'1',
Expand Down
9 changes: 6 additions & 3 deletions src/test/interpreters/activation/service.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,13 @@ suite('Interpreters Activation - Python Environment Variables', () => {

const shellCmd = capture(processService.shellExec).first()[0];

const isolated = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py');
const printEnvPyFile = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'printEnvVariables.py');
const expectedCommand = `${cmd.join(
' && '
)} && echo '${getEnvironmentPrefix}' && python ${printEnvPyFile.fileToCommandArgument()}`;
const expectedCommand = [
...cmd,
`echo '${getEnvironmentPrefix}'`,
`python ${isolated} ${printEnvPyFile.fileToCommandArgument()}`
].join(' && ');

expect(shellCmd).to.equal(expectedCommand);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ suite('Interpreters Activation - Python Environment Variables (using terminals)'
});
test('Should execute python file in terminal (that is what dumps variables into json)', async () => {
when(envVarsProvider.getCustomEnvironmentVariables(resource)).thenResolve(undefined);
const isolated = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py');
const pyFile = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'printEnvVariablesToFile.py');

await envActivationService.getActivatedEnvironmentVariables(resource, interpreter);
Expand All @@ -103,7 +104,11 @@ suite('Interpreters Activation - Python Environment Variables (using terminals)'
verify(
terminal.sendCommand(
cmd,
deepEqual([pyFile.fileToCommandArgument(), jsonFile.fileToCommandArgument()]),
deepEqual([
isolated.fileToCommandArgument(),
pyFile.fileToCommandArgument(),
jsonFile.fileToCommandArgument()
]),
anything(),
false
)
Expand Down
4 changes: 3 additions & 1 deletion src/test/providers/importSortProvider.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import { IServiceContainer } from '../../client/ioc/types';
import { SortImportsEditingProvider } from '../../client/providers/importSortProvider';
import { ISortImportsEditingProvider } from '../../client/providers/types';

const ISOLATED = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py');

suite('Import Sort Provider', () => {
let serviceContainer: TypeMoq.IMock<IServiceContainer>;
let shell: TypeMoq.IMock<IApplicationShell>;
Expand Down Expand Up @@ -398,7 +400,7 @@ suite('Import Sort Provider', () => {
.returns(() => Promise.resolve(processExeService.object))
.verifiable(TypeMoq.Times.once());
const importScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'sortImports.py');
const expectedArgs = [importScript, tmpFile.filePath, '--diff', '1', '2'];
const expectedArgs = [ISOLATED, importScript, tmpFile.filePath, '--diff', '1', '2'];
processExeService
.setup((p) =>
p.exec(TypeMoq.It.isValue(expectedArgs), TypeMoq.It.isValue({ throwOnStdErr: true, token: undefined }))
Expand Down
4 changes: 2 additions & 2 deletions src/test/testing/common/debugLauncher.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ suite('Unit Tests - Debug Launcher', () => {
expected = getDefaultDebugConfig();
}
expected.rules = [{ path: path.join(EXTENSION_ROOT_DIR, 'pythonFiles'), include: false }];
expected.program = testLaunchScript;
expected.args = options.args;
expected.program = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py');
expected.args = [testLaunchScript, ...options.args];
if (!expected.cwd) {
expected.cwd = workspaceFolders[0].uri.fsPath;
}
Expand Down
12 changes: 10 additions & 2 deletions src/test/testing/navigation/symbolNavigator.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ suite('Unit Tests - Navigation Command Handler', () => {
});
test('Ensure no symbols are returned when there are no symbols to be returned', async () => {
const docUri = Uri.file(__filename);
const args = [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'symbolProvider.py'), docUri.fsPath];
const args = [
path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py'),
path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'symbolProvider.py'),
docUri.fsPath
];
const proc: ExecutionResult<string> = {
stdout: JSON.stringify({ classes: [], methods: [], functions: [] })
};
Expand Down Expand Up @@ -114,7 +118,11 @@ suite('Unit Tests - Navigation Command Handler', () => {
});
test('Ensure symbols are returned', async () => {
const docUri = Uri.file(__filename);
const args = [path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'symbolProvider.py'), docUri.fsPath];
const args = [
path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pyvsc-run-isolated.py'),
path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'symbolProvider.py'),
docUri.fsPath
];
const proc: ExecutionResult<string> = {
stdout: JSON.stringify({
classes: [
Expand Down