diff --git a/requirements.txt b/requirements.txt index 0b17d7594a72..752eee82422f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ autopep8==1.2.1 yapf==0.6.2 -pylint==1.5.4 +pylint==1.8.2 pep8==1.7.0 prospector==0.11.7 flake8==2.6.0 diff --git a/src/client/interpreter/virtualEnvs/index.ts b/src/client/interpreter/virtualEnvs/index.ts index af5545398696..4f535cb99151 100644 --- a/src/client/interpreter/virtualEnvs/index.ts +++ b/src/client/interpreter/virtualEnvs/index.ts @@ -14,12 +14,12 @@ export class VirtualEnvironmentManager implements IVirtualEnvironmentManager { } public async getEnvironmentName(pythonPath: string): Promise { // https://stackoverflow.com/questions/1871549/determine-if-python-is-running-inside-virtualenv - const output = await this.processService.exec(pythonPath, ['-c', 'import sys;print(hasattr(sys, "real_prefix"))']); + // hasattr(sys, 'real_prefix') works for virtualenv while + // '(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))' works for venv + const code = 'import sys\nif hasattr(sys, "real_prefix"):\n print("virtualenv")\nelif hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix:\n print("venv")'; + const output = await this.processService.exec(pythonPath, ['-c', code]); if (output.stdout.length > 0) { - const result = output.stdout.trim(); - if (result === 'True') { - return 'virtualenv'; - } + return output.stdout.trim(); } return ''; } diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts index 742008ea7346..d941f73246b6 100644 --- a/src/client/linters/baseLinter.ts +++ b/src/client/linters/baseLinter.ts @@ -70,7 +70,7 @@ export abstract class BaseLinter implements ILinter { protected getWorkspaceRootPath(document: vscode.TextDocument): string { const workspaceFolder = this.workspace.getWorkspaceFolder(document.uri); const workspaceRootPath = (workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string') ? workspaceFolder.uri.fsPath : undefined; - return typeof workspaceRootPath === 'string' ? workspaceRootPath : __dirname; + return typeof workspaceRootPath === 'string' ? workspaceRootPath : path.dirname(document.uri.fsPath); } protected get logger(): ILogger { return this.serviceContainer.get(ILogger); diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts index b5aaf43c3743..196071a4d5ab 100644 --- a/src/client/linters/pylint.ts +++ b/src/client/linters/pylint.ts @@ -41,7 +41,8 @@ export class Pylint extends BaseLinter { && !await Pylint.hasConfigurationFile(this.fileSystem, this.getWorkspaceRootPath(document), this.platformService)) { minArgs = [ '--disable=all', - '--enable=F,E,unreachable,duplicate-key,unnecessary-semicolon,global-variable-not-assigned,unused-variable,unused-wildcard-import,binary-op-exception,bad-format-string,anomalous-backslash-in-string,bad-open-mode' + '--enable=F,E,unreachable,duplicate-key,unnecessary-semicolon,global-variable-not-assigned,unused-variable,unused-wildcard-import,binary-op-exception,bad-format-string,anomalous-backslash-in-string,bad-open-mode', + '–-disable=print-statement' ]; } const args = [ diff --git a/src/test/interpreters/virtualEnvManager.test.ts b/src/test/interpreters/virtualEnvManager.test.ts new file mode 100644 index 000000000000..bfd498b55de3 --- /dev/null +++ b/src/test/interpreters/virtualEnvManager.test.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { expect } from 'chai'; +import { Container } from 'inversify'; +import * as TypeMoq from 'typemoq'; +import { BufferDecoder } from '../../client/common/process/decoder'; +import { ProcessService } from '../../client/common/process/proc'; +import { IBufferDecoder, IProcessService } from '../../client/common/process/types'; +import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs'; +import { ServiceContainer } from '../../client/ioc/container'; +import { ServiceManager } from '../../client/ioc/serviceManager'; + +suite('Virtual environment manager', () => { + let serviceManager: ServiceManager; + let serviceContainer: ServiceContainer; + let process: TypeMoq.IMock; + + setup(async () => { + const cont = new Container(); + serviceManager = new ServiceManager(cont); + serviceContainer = new ServiceContainer(cont); + }); + + test('Plain Python environment suffix', async () => await testSuffix('')); + test('Venv environment suffix', async () => await testSuffix('venv')); + test('Virtualenv Python environment suffix', async () => await testSuffix('virtualenv')); + + test('Run actual virtual env detection code', async () => { + serviceManager.addSingleton(IProcessService, ProcessService); + serviceManager.addSingleton(IBufferDecoder, BufferDecoder); + const venvManager = new VirtualEnvironmentManager(serviceContainer); + const name = await venvManager.getEnvironmentName('python'); + const result = name === '' || name === 'venv' || name === 'virtualenv'; + expect(result).to.be.equal(true, 'Running venv detection code failed.'); + }); + + async function testSuffix(expectedName: string) { + process = TypeMoq.Mock.ofType(); + serviceManager.addSingletonInstance(IProcessService, process.object); + + const venvManager = new VirtualEnvironmentManager(serviceContainer); + process + .setup(x => x.exec('python', TypeMoq.It.isAny())) + .returns(() => Promise.resolve({ + stdout: expectedName, + stderr: '' + })); + + const name = await venvManager.getEnvironmentName('python'); + expect(name).to.be.equal(expectedName, 'Virtual envrironment name suffix is incorrect.'); + } +}); diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index 65b160da49a9..8e73a2878abf 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -231,7 +231,7 @@ suite('Linting', () => { }); test('PyLint with config in root', async () => { await fs.copy(path.join(pylintConfigPath, '.pylintrc'), path.join(workspaceUri.fsPath, '.pylintrc')); - await testLinterMessages(Product.pylint, path.join(pylintConfigPath, 'file.py'), []); + await testLinterMessages(Product.pylint, path.join(pylintConfigPath, 'file2.py'), []); }); test('Flake8 with config in root', async () => { await testLinterMessages(Product.flake8, path.join(flake8ConfigPath, 'file.py'), filteredFlake8MessagesToBeReturned); diff --git a/src/test/pythonFiles/linting/pylintconfig/file2.py b/src/test/pythonFiles/linting/pylintconfig/file2.py new file mode 100644 index 000000000000..f375c984aa2e --- /dev/null +++ b/src/test/pythonFiles/linting/pylintconfig/file2.py @@ -0,0 +1,19 @@ +"""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"