diff --git a/CHANGELOG.md b/CHANGELOG.md index e1839daae64e..1a3f1a5c9f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,69 @@ # Changelog +## 2020.8.1 (20 August 2020) + +### Fixes + +1. Update LSP to latest to resolve problems with LS settings. + ([#13511](https://github.com/microsoft/vscode-python/pull/13511)) +1. Update debugger to address terminal input issues. +1. Added tooltip to indicate status of server connection + ([#13543](https://github.com/Microsoft/vscode-python/issues/13543)) + +### Thanks + +Thanks to the following projects which we fully rely on to provide some of +our features: + +- [debugpy](https://pypi.org/project/debugpy/) +- [isort](https://pypi.org/project/isort/) +- [jedi](https://pypi.org/project/jedi/) + and [parso](https://pypi.org/project/parso/) +- [Microsoft Python Language Server](https://github.com/microsoft/python-language-server) +- [Pylance](https://github.com/microsoft/pylance-release) +- [exuberant ctags](http://ctags.sourceforge.net/) (user-installed) +- [rope](https://pypi.org/project/rope/) (user-installed) + +Also thanks to the various projects we provide integrations with which help +make this extension useful: + +- Debugging support: + [Django](https://pypi.org/project/Django/), + [Flask](https://pypi.org/project/Flask/), + [gevent](https://pypi.org/project/gevent/), + [Jinja](https://pypi.org/project/Jinja/), + [Pyramid](https://pypi.org/project/pyramid/), + [PySpark](https://pypi.org/project/pyspark/), + [Scrapy](https://pypi.org/project/Scrapy/), + [Watson](https://pypi.org/project/Watson/) +- Formatting: + [autopep8](https://pypi.org/project/autopep8/), + [black](https://pypi.org/project/black/), + [yapf](https://pypi.org/project/yapf/) +- Interpreter support: + [conda](https://conda.io/), + [direnv](https://direnv.net/), + [pipenv](https://pypi.org/project/pipenv/), + [pyenv](https://github.com/pyenv/pyenv), + [venv](https://docs.python.org/3/library/venv.html#module-venv), + [virtualenv](https://pypi.org/project/virtualenv/) +- Linting: + [bandit](https://pypi.org/project/bandit/), + [flake8](https://pypi.org/project/flake8/), + [mypy](https://pypi.org/project/mypy/), + [prospector](https://pypi.org/project/prospector/), + [pylint](https://pypi.org/project/pylint/), + [pydocstyle](https://pypi.org/project/pydocstyle/), + [pylama](https://pypi.org/project/pylama/) +- Testing: + [nose](https://pypi.org/project/nose/), + [pytest](https://pypi.org/project/pytest/), + [unittest](https://docs.python.org/3/library/unittest.html#module-unittest) + +And finally thanks to the [Python](https://www.python.org/) development team and +community for creating a fantastic programming language and community to be a +part of! + ## 2020.8.0 (12 August 2020) ### Enhancements diff --git a/package-lock.json b/package-lock.json index 2b3901683137..d18fdc0009b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "python", - "version": "2020.8.0", + "version": "2020.8.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -27217,94 +27217,47 @@ } }, "vscode-jsonrpc": { - "version": "6.0.0-next.3", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz", - "integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA==" + "version": "6.0.0-next.4", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.4.tgz", + "integrity": "sha512-nIXUeSMKfqvqSG75s1JuUy0UgjE3d0NERjGz7AUGDFXX0/roVUEbaio5VBdus30nty8v0FCf/6MbCSrH5RybcQ==" }, "vscode-languageclient": { - "version": "7.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.6.tgz", - "integrity": "sha512-3CPGOXCSxpPGONNVg+ZhY4AYyJrjLDJSKJT2acJ6L5mSlzKACFkFd3yQEGVHS/O0v4n/9i/KRNpBGVSR8ngw7Q==", + "version": "7.0.0-next.8", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.8.tgz", + "integrity": "sha512-qgQBImLm24tjhvc42YbLLRYWibwSgccUruy0GhVOr69BZRfT3CkMeeQfjIdumrfCCYPu/vPdJ6KjtAYamBCESw==", "requires": { "semver": "^6.3.0", - "vscode-languageserver-protocol": "3.16.0-next.5" + "vscode-languageserver-protocol": "3.16.0-next.6" }, "dependencies": { "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "vscode-jsonrpc": { - "version": "6.0.0-next.3", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz", - "integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA==" - }, - "vscode-languageserver-protocol": { - "version": "3.16.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.5.tgz", - "integrity": "sha512-MVuLT4d5tdDo/14laViyZY+p1HT7wYDwJdvGmo8mCfkgON5c10AABhtqjD+LccidBcwdbKy+4dwwUUxiNg/8PA==", - "requires": { - "vscode-jsonrpc": "6.0.0-next.3", - "vscode-languageserver-types": "3.16.0-next.2" - } - }, - "vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" } } }, "vscode-languageserver": { - "version": "7.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0-next.4.tgz", - "integrity": "sha512-yyFMuKLNkSKYb//6dQHCo0wlDYWkgRPEXoV+/aYXHp9ziIBqQxKJJFrf/rnXYReCz1TroxYMcFGdO5UrBsLAaA==", + "version": "7.0.0-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0-next.6.tgz", + "integrity": "sha512-arS+G5+hZ1MXOk+RDLzBPncJdyngpgHYUO7Oa5XQtQlmCuIktcq8fzNxCbpe12oeP1VtiuVcmNMnafkbKI5INQ==", "requires": { - "vscode-languageserver-protocol": "3.16.0-next.5" - }, - "dependencies": { - "vscode-jsonrpc": { - "version": "6.0.0-next.3", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz", - "integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA==" - }, - "vscode-languageserver-protocol": { - "version": "3.16.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.5.tgz", - "integrity": "sha512-MVuLT4d5tdDo/14laViyZY+p1HT7wYDwJdvGmo8mCfkgON5c10AABhtqjD+LccidBcwdbKy+4dwwUUxiNg/8PA==", - "requires": { - "vscode-jsonrpc": "6.0.0-next.3", - "vscode-languageserver-types": "3.16.0-next.2" - } - }, - "vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" - } + "vscode-languageserver-protocol": "3.16.0-next.6" } }, "vscode-languageserver-protocol": { - "version": "3.16.0-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.5.tgz", - "integrity": "sha512-MVuLT4d5tdDo/14laViyZY+p1HT7wYDwJdvGmo8mCfkgON5c10AABhtqjD+LccidBcwdbKy+4dwwUUxiNg/8PA==", + "version": "3.16.0-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.6.tgz", + "integrity": "sha512-tW8LMFBBr6WJ33mzYAjiLTu/4tch9nvJ+rBhUNzJ8X1zNR39vj8CV3FsnrxzAqvOB+qBWEOQa0tjupW1mNo2GQ==", "requires": { - "vscode-jsonrpc": "6.0.0-next.3", - "vscode-languageserver-types": "3.16.0-next.2" - }, - "dependencies": { - "vscode-jsonrpc": { - "version": "6.0.0-next.3", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.3.tgz", - "integrity": "sha512-bkAtRMXbTwU1pAXnlbkAMnrqXZX3q2/zttkcnPZOsDqgoycO2L7GK2aN3K5qCMZmhItwBU185T78Q9YH0v5jEA==" - } + "vscode-jsonrpc": "6.0.0-next.4", + "vscode-languageserver-types": "3.16.0-next.3" } }, "vscode-languageserver-types": { - "version": "3.16.0-next.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", - "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" + "version": "3.16.0-next.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.3.tgz", + "integrity": "sha512-s/z5ZqSe7VpoXJ6JQcvwRiPPA3nG0nAcJ/HH03zoU6QaFfnkcgPK+HshC3WKPPnC2G08xA0iRB6h7kmyBB5Adg==" }, "vscode-tas-client": { "version": "0.0.864", diff --git a/package.json b/package.json index d1588494ff6c..bd6c7c392b48 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "python", "displayName": "Python", "description": "Linting, Debugging (multi-threaded, remote), Intellisense, Jupyter Notebooks, code formatting, refactoring, unit tests, snippets, and more.", - "version": "2020.8.0", + "version": "2020.8.1", "featureFlags": { "usingNewInterpreterStorage": true }, @@ -3523,10 +3523,10 @@ "vscode-debugadapter": "^1.28.0", "vscode-debugprotocol": "^1.28.0", "vscode-extension-telemetry": "0.1.4", - "vscode-jsonrpc": "^6.0.0-next.3", - "vscode-languageclient": "^7.0.0-next.6", - "vscode-languageserver": "^7.0.0-next.4", - "vscode-languageserver-protocol": "^3.16.0-next.5", + "vscode-jsonrpc": "^6.0.0-next.4", + "vscode-languageclient": "^7.0.0-next.8", + "vscode-languageserver": "^7.0.0-next.6", + "vscode-languageserver-protocol": "^3.16.0-next.6", "vscode-tas-client": "^0.0.864", "vsls": "^0.3.1291", "winreg": "^1.2.4", diff --git a/package.nls.json b/package.nls.json index 53bb5b2a73d0..2c096738b27d 100644 --- a/package.nls.json +++ b/package.nls.json @@ -179,6 +179,8 @@ "DataScience.connectingToJupyter": "Connecting to Jupyter server", "DataScience.connectingToIPyKernel": "Connecting to IPython kernel", "DataScience.connectedToIPyKernel": "Connected.", + "DataScience.connected": "Connected", + "DataScience.disconnected": "Disconnected", "Experiments.inGroup": "User belongs to experiment group '{0}'", "Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters", "Interpreters.entireWorkspace": "Entire workspace", diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index ff54886b9165..e8b062064f18 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -1106,6 +1106,8 @@ export namespace DataScience { 'DataScience.previewNotebookOnlySupportedInVSCInsiders', 'The Preview Notebook Editor is supported only in the Insiders version of Visual Studio Code.' ); + export const connected = localize('DataScience.connected', 'Connected'); + export const disconnected = localize('DataScience.disconnected', 'Disconnected'); } export namespace StartPage { diff --git a/src/client/languageServices/proposeLanguageServerBanner.ts b/src/client/languageServices/proposeLanguageServerBanner.ts index 13acc4527005..59096edc2271 100644 --- a/src/client/languageServices/proposeLanguageServerBanner.ts +++ b/src/client/languageServices/proposeLanguageServerBanner.ts @@ -17,6 +17,8 @@ import { IPythonExtensionBanner } from '../common/types'; import { Common, Pylance } from '../common/utils/localize'; +import { sendTelemetryEvent } from '../telemetry'; +import { EventName } from '../telemetry/constants'; export function getPylanceExtensionUri(appEnv: IApplicationEnvironment): string { return `${appEnv.uriScheme}:extension/${PYLANCE_EXTENSION_ID}`; @@ -72,14 +74,19 @@ export class ProposePylanceBanner implements IPythonExtensionBanner { Pylance.remindMeLater() ); + let userAction: string; if (response === Pylance.tryItNow()) { this.appShell.openUrl(getPylanceExtensionUri(this.appEnv)); + userAction = 'yes'; await this.disable(); } else if (response === Common.bannerLabelNo()) { await this.disable(); + userAction = 'no'; } else { this.disabledInCurrentSession = true; + userAction = 'later'; } + sendTelemetryEvent(EventName.LANGUAGE_SERVER_TRY_PYLANCE, undefined, { userAction }); } public async shouldShowBanner(): Promise { diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index f0f38e6ac69a..766e2f0e2f01 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -93,6 +93,7 @@ export enum EventName { LANGUAGE_SERVER_READY = 'LANGUAGE_SERVER.READY', LANGUAGE_SERVER_TELEMETRY = 'LANGUAGE_SERVER.EVENT', LANGUAGE_SERVER_REQUEST = 'LANGUAGE_SERVER.REQUEST', + LANGUAGE_SERVER_TRY_PYLANCE = 'LANGUAGE_SERVER.TRY_PYLANCE', TERMINAL_CREATE = 'TERMINAL.CREATE', ACTIVATE_ENV_IN_CURRENT_TERMINAL = 'ACTIVATE_ENV_IN_CURRENT_TERMINAL', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 74c0046580cd..cec904f17e8b 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1362,6 +1362,16 @@ export interface IEventNamePropertyMapping { * Telemetry sent when the client makes a request to the Node.js server */ [EventName.LANGUAGE_SERVER_REQUEST]: any; + /** + * Telemetry sent on user response to 'Try Pylance' prompt. + */ + [EventName.LANGUAGE_SERVER_TRY_PYLANCE]: { + /** + * User response to the prompt. + * @type {string} + */ + userAction: string; + }; /** * Telemetry captured for enabling reload. */ diff --git a/src/datascience-ui/interactive-common/jupyterInfo.tsx b/src/datascience-ui/interactive-common/jupyterInfo.tsx index be4fa975691d..441f477f255b 100644 --- a/src/datascience-ui/interactive-common/jupyterInfo.tsx +++ b/src/datascience-ui/interactive-common/jupyterInfo.tsx @@ -61,6 +61,7 @@ export class JupyterInfo extends React.Component { baseTheme={this.props.baseTheme} class="image-button-image kernel-status-icon" image={this.getIcon()} + title={this.getStatus()} />
@@ -113,4 +114,10 @@ export class JupyterInfo extends React.Component { ? ImageName.JupyterServerDisconnected : ImageName.JupyterServerConnected; } + + private getStatus() { + return this.props.kernel.jupyterServerStatus === ServerStatus.NotStarted + ? getLocString('DataScience.disconnected', 'Disconnected') + : getLocString('DataScience.connected', 'Connected'); + } } diff --git a/src/datascience-ui/react-common/image.tsx b/src/datascience-ui/react-common/image.tsx index 740e6aa5d66d..40b09332a062 100644 --- a/src/datascience-ui/react-common/image.tsx +++ b/src/datascience-ui/react-common/image.tsx @@ -238,6 +238,7 @@ interface IImageProps { baseTheme: string; image: ImageName; class: string; + title?: string; } export class Image extends React.Component { @@ -249,6 +250,6 @@ export class Image extends React.Component { const key = ImageName[this.props.image].toString(); const image = images.hasOwnProperty(key) ? images[key] : images.Cancel; // Default is cancel. const source = this.props.baseTheme.includes('dark') ? image.dark : image.light; - return ; + return ; } } diff --git a/src/test/.vscode/settings.json b/src/test/.vscode/settings.json index f997865a5301..3d68e9e0284c 100644 --- a/src/test/.vscode/settings.json +++ b/src/test/.vscode/settings.json @@ -24,7 +24,6 @@ "python.linting.banditEnabled": false, "python.formatting.provider": "yapf", "python.linting.pylintUseMinimalCheckers": false, - "python.pythonPath": "python", // Do not set this to "Microsoft", else it will result in LS being downloaded on CI // and that slows down tests significantly. We have other tests on CI for testing // downloading of LS with this setting enabled. diff --git a/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts b/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts index b7e9c56cd7c2..7dcfda29a19d 100644 --- a/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts +++ b/src/test/testing/banners/proposeNewLanguageServerBanner.unit.test.ts @@ -2,8 +2,8 @@ // Licensed under the MIT License. 'use strict'; - -import { expect } from 'chai'; +import { assert, expect } from 'chai'; +import * as sinon from 'sinon'; import * as typemoq from 'typemoq'; import { Extension } from 'vscode'; import { LanguageServerType } from '../../../client/activation/types'; @@ -24,6 +24,8 @@ import { ProposeLSStateKeys, ProposePylanceBanner } from '../../../client/languageServices/proposeLanguageServerBanner'; +import * as Telemetry from '../../../client/telemetry'; +import { EventName } from '../../../client/telemetry/constants'; interface IExperimentLsCombination { inExperiment: boolean; @@ -46,6 +48,8 @@ suite('Propose Pylance Banner', () => { let appShell: typemoq.IMock; let appEnv: typemoq.IMock; let settings: typemoq.IMock; + let sendTelemetryStub: sinon.SinonStub; + let telemetryEvent: { eventName: EventName; properties: { userAction: string } } | undefined; const message = Pylance.proposePylanceMessage(); const yes = Pylance.tryItNow(); @@ -59,7 +63,23 @@ suite('Propose Pylance Banner', () => { appShell = typemoq.Mock.ofType(); appEnv = typemoq.Mock.ofType(); appEnv.setup((x) => x.uriScheme).returns(() => 'scheme'); + + sendTelemetryStub = sinon + .stub(Telemetry, 'sendTelemetryEvent') + .callsFake((eventName: EventName, _, properties: { userAction: string }) => { + telemetryEvent = { + eventName, + properties + }; + }); + }); + + teardown(() => { + telemetryEvent = undefined; + sinon.restore(); + Telemetry._resetSharedProperties(); }); + testData.forEach((t) => { test(`${t.inExperiment ? 'In' : 'Not in'} experiment and "python.languageServer": "${t.lsType}" should ${ t.shouldShowBanner ? 'show' : 'not show' @@ -109,8 +129,15 @@ suite('Propose Pylance Banner', () => { const testBanner = preparePopup(true, appShell.object, appEnv.object, config.object, true, false); await testBanner.showBanner(); + expect(testBanner.enabled).to.be.equal(false, 'Banner should be permanently disabled when user clicked No'); appShell.verifyAll(); + + sinon.assert.calledOnce(sendTelemetryStub); + assert.deepEqual(telemetryEvent, { + eventName: EventName.LANGUAGE_SERVER_TRY_PYLANCE, + properties: { userAction: 'no' } + }); }); test('Clicking Later should disable banner in session', async () => { appShell @@ -128,11 +155,20 @@ suite('Propose Pylance Banner', () => { const testBanner = preparePopup(true, appShell.object, appEnv.object, config.object, true, false); await testBanner.showBanner(); + expect(testBanner.enabled).to.be.equal( true, 'Banner should not be permanently disabled when user clicked Later' ); appShell.verifyAll(); + + sinon.assert.calledOnce(sendTelemetryStub); + assert.deepEqual(telemetryEvent, { + eventName: EventName.LANGUAGE_SERVER_TRY_PYLANCE, + properties: { + userAction: 'later' + } + }); }); test('Clicking Yes opens the extension marketplace entry', async () => { appShell @@ -150,8 +186,17 @@ suite('Propose Pylance Banner', () => { const testBanner = preparePopup(true, appShell.object, appEnv.object, config.object, true, false); await testBanner.showBanner(); + expect(testBanner.enabled).to.be.equal(false, 'Banner should be permanently disabled after opening store URL'); appShell.verifyAll(); + + sinon.assert.calledOnce(sendTelemetryStub); + assert.deepEqual(telemetryEvent, { + eventName: EventName.LANGUAGE_SERVER_TRY_PYLANCE, + properties: { + userAction: 'yes' + } + }); }); });