Skip to content

Commit 6895143

Browse files
authored
Cherry pick conda and mypy into release-2019.12 (#9530)
* Revert calling mypy with relatives paths (regression) (#9502) * Revert "fix(client/linters/mypy): call mypy incorrectly (#5834)" This reverts commit 1b6fbfb. * News file * Revert "Fix MyPy CI tests (#8518)" This reverts commit c634ffd. * Disable conda execution service instantiation (#9493) * Disable conda execution service instantiation * News file * Didn't mean to commit that * Forgot a few tests * Linting * Update comment * Linting * Test failure caused by linting * Undo tslint formatting change
1 parent c1c5552 commit 6895143

File tree

8 files changed

+70
-137
lines changed

8 files changed

+70
-137
lines changed

news/2 Fixes/9490.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Disable use of `conda run`.

news/2 Fixes/9496.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Revert changes related to calling `mypy` with relative paths.

src/client/common/process/pythonExecutionFactory.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,6 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
5252
const processLogger = this.serviceContainer.get<IProcessLogger>(IProcessLogger);
5353
processService.on('exec', processLogger.logProcess.bind(processLogger));
5454

55-
// Don't bother getting a conda execution service instance if we haven't fetched the list of interpreters yet.
56-
// Also, without this hasInterpreters check smoke tests will time out
57-
const interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
58-
const hasInterpreters = await interpreterService.hasInterpreters;
59-
if (hasInterpreters) {
60-
const condaExecutionService = await this.createCondaExecutionService(pythonPath, processService);
61-
if (condaExecutionService) {
62-
return condaExecutionService;
63-
}
64-
}
65-
6655
if (this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)) {
6756
return new WindowsStorePythonProcess(this.serviceContainer, processService, pythonPath, this.windowsStoreInterpreter);
6857
}
@@ -83,7 +72,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
8372

8473
// No daemon support in Python 2.7.
8574
const interpreter = await interpreterService.getInterpreterDetails(pythonPath);
86-
if (interpreter?.version && interpreter.version.major < 3){
75+
if (interpreter?.version && interpreter.version.major < 3) {
8776
return activatedProcPromise!;
8877
}
8978

@@ -95,7 +84,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
9584
this.activationHelper.getActivatedEnvironmentVariables(options.resource, interpreter, true)
9685
]);
9786

98-
const daemon = new PythonDaemonExecutionServicePool(logger, disposables, {...options, pythonPath}, activatedProc!, activatedEnvVars);
87+
const daemon = new PythonDaemonExecutionServicePool(logger, disposables, { ...options, pythonPath }, activatedProc!, activatedEnvVars);
9988
await daemon.initialize();
10089
disposables.push(daemon);
10190
return daemon;
@@ -128,16 +117,10 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
128117
processService.on('exec', processLogger.logProcess.bind(processLogger));
129118
this.serviceContainer.get<IDisposableRegistry>(IDisposableRegistry).push(processService);
130119

131-
// Allow parts of the code to ignore conda run.
132-
if (!options.bypassCondaExecution) {
133-
const condaExecutionService = await this.createCondaExecutionService(pythonPath, processService);
134-
if (condaExecutionService) {
135-
return condaExecutionService;
136-
}
137-
}
138-
139120
return new PythonExecutionService(this.serviceContainer, processService, pythonPath);
140121
}
122+
// Not using this function for now because there are breaking issues with conda run (conda 4.8, PVSC 2020.1).
123+
// See https://github.com/microsoft/vscode-python/issues/9490
141124
public async createCondaExecutionService(pythonPath: string, processService?: IProcessService, resource?: Uri): Promise<CondaExecutionService | undefined> {
142125
const processServicePromise = processService ? Promise.resolve(processService) : this.processServiceFactory.create(resource);
143126
const [condaVersion, condaEnvironment, condaFile, procService] = await Promise.all([

src/client/linters/mypy.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as path from 'path';
21
import { CancellationToken, OutputChannel, TextDocument } from 'vscode';
32
import '../common/extensions';
43
import { Product } from '../common/types';
@@ -14,9 +13,7 @@ export class MyPy extends BaseLinter {
1413
}
1514

1615
protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise<ILintMessage[]> {
17-
const cwd = this.getWorkspaceRootPath(document);
18-
const relativePath = path.relative(cwd, document.uri.fsPath);
19-
const messages = await this.run([relativePath], document, cancellation, REGEX);
16+
const messages = await this.run([document.uri.fsPath], document, cancellation, REGEX);
2017
messages.forEach(msg => {
2118
msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.mypyCategorySeverity);
2219
msg.code = msg.type;

src/test/common/process/condaExecutionService.unit.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ suite('CondaExecutionService', () => {
2424
serviceContainer.setup(s => s.get<IFileSystem>(IFileSystem)).returns(() => fileSystem.object);
2525
});
2626

27-
test('getExecutionInfo with a named environment should return execution info using the environment name', () => {
27+
test('getExecutionInfo with a named environment should return execution info using the environment name', function() {
28+
// tslint:disable-next-line:no-invalid-this
29+
return this.skip();
30+
2831
const environment = { name: 'foo', path: 'bar' };
2932
executionService = new CondaExecutionService(serviceContainer.object, processService.object, pythonPath, condaFile, environment);
3033

@@ -33,7 +36,10 @@ suite('CondaExecutionService', () => {
3336
expect(result).to.deep.equal({ command: condaFile, args: ['run', '-n', environment.name, 'python', ...args] });
3437
});
3538

36-
test('getExecutionInfo with a non-named environment should return execution info using the environment path', async () => {
39+
test('getExecutionInfo with a non-named environment should return execution info using the environment path', async function() {
40+
// tslint:disable-next-line:no-invalid-this
41+
return this.skip();
42+
3743
const environment = { name: '', path: 'bar' };
3844
executionService = new CondaExecutionService(serviceContainer.object, processService.object, pythonPath, condaFile, environment);
3945

src/test/common/process/pythonExecutionFactory.unit.test.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ suite('Process - PythonExecutionFactory', () => {
196196
expect(service).instanceOf(WindowsStorePythonProcess);
197197
});
198198

199-
test('Ensure `create` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async () => {
199+
test('Ensure `create` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function() {
200+
// tslint:disable-next-line:no-invalid-this
201+
return this.skip();
202+
200203
const pythonPath = 'path/to/python';
201204
const pythonSettings = mock(PythonSettings);
202205

@@ -218,7 +221,10 @@ suite('Process - PythonExecutionFactory', () => {
218221
expect(service).instanceOf(CondaExecutionService);
219222
});
220223

221-
test('Ensure `create` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async () => {
224+
test('Ensure `create` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function() {
225+
// tslint:disable-next-line:no-invalid-this
226+
return this.skip();
227+
222228
const pythonPath = 'path/to/python';
223229
const pythonSettings = mock(PythonSettings);
224230
when(processFactory.create(resource)).thenResolve(processService.object);
@@ -237,7 +243,10 @@ suite('Process - PythonExecutionFactory', () => {
237243
expect(service).instanceOf(PythonExecutionService);
238244
});
239245

240-
test('Ensure `createActivatedEnvironment` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async () => {
246+
test('Ensure `createActivatedEnvironment` returns a CondaExecutionService instance if createCondaExecutionService() returns a valid object', async function() {
247+
// tslint:disable-next-line:no-invalid-this
248+
return this.skip();
249+
241250
const pythonPath = 'path/to/python';
242251
const pythonSettings = mock(PythonSettings);
243252

@@ -256,13 +265,17 @@ suite('Process - PythonExecutionFactory', () => {
256265
verify(pythonSettings.pythonPath).once();
257266
verify(condaService.getCondaEnvironment(pythonPath)).once();
258267
} else {
268+
// @ts-ignore
259269
verify(condaService.getCondaEnvironment(interpreter.path)).once();
260270
}
261271

262272
expect(service).instanceOf(CondaExecutionService);
263273
});
264274

265-
test('Ensure `createActivatedEnvironment` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async () => {
275+
test('Ensure `createActivatedEnvironment` returns a PythonExecutionService instance if createCondaExecutionService() returns undefined', async function() {
276+
// tslint:disable-next-line:no-invalid-this
277+
return this.skip();
278+
266279
let createInvoked = false;
267280
const pythonPath = 'path/to/python';
268281
const mockExecService = 'mockService';

src/test/linters/lint.args.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ suite('Linting - Arguments', () => {
137137
});
138138
test('MyPy', async () => {
139139
const linter = new MyPy(outputChannel.object, serviceContainer);
140-
const expectedPath = workspaceUri ? path.join(path.basename(path.dirname(fileUri.fsPath)), path.basename(fileUri.fsPath)) : path.basename(fileUri.fsPath);
141-
const expectedArgs = [expectedPath];
140+
const expectedArgs = [fileUri.fsPath];
142141
await testLinter(linter, expectedArgs);
143142
});
144143
test('Pydocstyle', async () => {

src/test/linters/mypy.unit.test.ts

Lines changed: 37 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,12 @@
66
// tslint:disable:no-object-literal-type-assertion
77

88
import { expect } from 'chai';
9-
import * as path from 'path';
10-
import * as sinon from 'sinon';
11-
import { anything, instance, mock, when } from 'ts-mockito';
12-
import { CancellationToken, CancellationTokenSource, TextDocument, Uri } from 'vscode';
13-
import { Product } from '../../client/common/types';
14-
import { ServiceContainer } from '../../client/ioc/container';
159
import { parseLine } from '../../client/linters/baseLinter';
16-
import { LinterManager } from '../../client/linters/linterManager';
17-
import { MyPy, REGEX } from '../../client/linters/mypy';
18-
import { ILinterManager, ILintMessage, LintMessageSeverity } from '../../client/linters/types';
19-
import { MockOutputChannel } from '../mockClasses';
10+
import { REGEX } from '../../client/linters/mypy';
11+
import { ILintMessage } from '../../client/linters/types';
2012

2113
// This following is a real-world example. See gh=2380.
22-
// tslint:disable:no-multiline-string no-any max-func-body-length
14+
// tslint:disable-next-line:no-multiline-string
2315
const output = `
2416
provider.pyi:10: error: Incompatible types in assignment (expression has type "str", variable has type "int")
2517
provider.pyi:11: error: Name 'not_declared_var' is not defined
@@ -30,30 +22,40 @@ suite('Linting - MyPy', () => {
3022
test('regex', async () => {
3123
const lines = output.split('\n');
3224
const tests: [string, ILintMessage][] = [
33-
[lines[1], {
34-
code: undefined,
35-
message: 'Incompatible types in assignment (expression has type "str", variable has type "int")',
36-
column: 0,
37-
line: 10,
38-
type: 'error',
39-
provider: 'mypy'
40-
} as ILintMessage],
41-
[lines[2], {
42-
code: undefined,
43-
message: 'Name \'not_declared_var\' is not defined',
44-
column: 0,
45-
line: 11,
46-
type: 'error',
47-
provider: 'mypy'
48-
} as ILintMessage],
49-
[lines[3], {
50-
code: undefined,
51-
message: 'Expression has type "Any"',
52-
column: 21,
53-
line: 12,
54-
type: 'error',
55-
provider: 'mypy'
56-
} as ILintMessage]
25+
[
26+
lines[1],
27+
{
28+
code: undefined,
29+
message: 'Incompatible types in assignment (expression has type "str", variable has type "int")',
30+
column: 0,
31+
line: 10,
32+
type: 'error',
33+
provider: 'mypy'
34+
} as ILintMessage
35+
],
36+
[
37+
lines[2],
38+
{
39+
code: undefined,
40+
// tslint:disable-next-line
41+
message: "Name 'not_declared_var' is not defined",
42+
column: 0,
43+
line: 11,
44+
type: 'error',
45+
provider: 'mypy'
46+
} as ILintMessage
47+
],
48+
[
49+
lines[3],
50+
{
51+
code: undefined,
52+
message: 'Expression has type "Any"',
53+
column: 21,
54+
line: 12,
55+
type: 'error',
56+
provider: 'mypy'
57+
} as ILintMessage
58+
]
5759
];
5860
for (const [line, expected] of tests) {
5961
const msg = parseLine(line, REGEX, 'mypy');
@@ -62,72 +64,3 @@ suite('Linting - MyPy', () => {
6264
}
6365
});
6466
});
65-
66-
suite('Test Linter', () => {
67-
class TestMyPyLinter extends MyPy {
68-
// tslint:disable: no-unnecessary-override
69-
public async runLinter(document: TextDocument, cancellation: CancellationToken): Promise<ILintMessage[]> {
70-
return super.runLinter(document, cancellation);
71-
}
72-
public getWorkspaceRootPath(document: TextDocument): string {
73-
return super.getWorkspaceRootPath(document);
74-
}
75-
public async run(args: string[], document: TextDocument, cancellation: CancellationToken, regEx: string = REGEX): Promise<ILintMessage[]> {
76-
return super.run(args, document, cancellation, regEx);
77-
}
78-
public parseMessagesSeverity(error: string, severity: any): LintMessageSeverity {
79-
return super.parseMessagesSeverity(error, severity);
80-
}
81-
}
82-
83-
let linter: TestMyPyLinter;
84-
let getWorkspaceRootPathStub: sinon.SinonStub<[TextDocument], string>;
85-
let runStub: sinon.SinonStub<[string[], TextDocument, CancellationToken, (string | undefined)?], Promise<ILintMessage[]>>;
86-
const token = new CancellationTokenSource().token;
87-
teardown(() => sinon.restore());
88-
setup(() => {
89-
const linterManager = mock(LinterManager);
90-
when(linterManager.getLinterInfo(anything())).thenReturn({ product: Product.mypy } as any);
91-
const serviceContainer = mock(ServiceContainer);
92-
when(serviceContainer.get<ILinterManager>(ILinterManager)).thenReturn(instance(linterManager));
93-
getWorkspaceRootPathStub = sinon.stub(TestMyPyLinter.prototype, 'getWorkspaceRootPath');
94-
runStub = sinon.stub(TestMyPyLinter.prototype, 'run');
95-
linter = new TestMyPyLinter(instance(mock(MockOutputChannel)), instance(serviceContainer));
96-
});
97-
98-
test('Get cwd based on document', async () => {
99-
const fileUri = Uri.file(path.join('a', 'b', 'c', 'd', 'e', 'filename.py'));
100-
const cwd = path.join('a', 'b', 'c');
101-
const doc = { uri: fileUri } as any as TextDocument;
102-
getWorkspaceRootPathStub.callsFake(() => cwd);
103-
runStub.callsFake(() => Promise.resolve([]));
104-
105-
await linter.runLinter(doc, token);
106-
107-
expect(getWorkspaceRootPathStub.callCount).to.equal(1);
108-
expect(getWorkspaceRootPathStub.args[0]).to.deep.equal([doc]);
109-
});
110-
test('Pass relative path of document to linter', async () => {
111-
const fileUri = Uri.file(path.join('a', 'b', 'c', 'd', 'e', 'filename.py'));
112-
const cwd = path.join('a', 'b', 'c');
113-
const doc = { uri: fileUri } as any as TextDocument;
114-
getWorkspaceRootPathStub.callsFake(() => cwd);
115-
runStub.callsFake(() => Promise.resolve([]));
116-
117-
await linter.runLinter(doc, token);
118-
119-
expect(runStub.callCount).to.equal(1);
120-
expect(runStub.args[0]).to.deep.equal([[path.relative(cwd, fileUri.fsPath)], doc, token, REGEX]);
121-
});
122-
test('Return empty messages', async () => {
123-
const fileUri = Uri.file(path.join('a', 'b', 'c', 'd', 'e', 'filename.py'));
124-
const cwd = path.join('a', 'b', 'c');
125-
const doc = { uri: fileUri } as any as TextDocument;
126-
getWorkspaceRootPathStub.callsFake(() => cwd);
127-
runStub.callsFake(() => Promise.resolve([]));
128-
129-
const messages = await linter.runLinter(doc, token);
130-
131-
expect(messages).to.be.deep.equal([]);
132-
});
133-
});

0 commit comments

Comments
 (0)