diff --git a/.github/release_plan.md b/.github/release_plan.md index 076cd64132dd..bc9e623bc774 100644 --- a/.github/release_plan.md +++ b/.github/release_plan.md @@ -24,8 +24,10 @@ Feature freeze is Monday @ 17:00 America/Vancouver, XXX XX. At that point, commi -# Release candidate (Monday, XXX XX) +# Release candidate (Thursday, XXX XX) +NOTE: This Thursday occurs during TESTING week. Branching should be done during this week to freeze the release with only the correct changes. Any last minute fixes go in as candidates into the release branch and will require team approval. +Other: 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]. diff --git a/build/azure-pipeline.stable.yml b/build/azure-pipeline.stable.yml index ef8f501b6e5a..a5276bbb09d2 100644 --- a/build/azure-pipeline.stable.yml +++ b/build/azure-pipeline.stable.yml @@ -102,7 +102,7 @@ extends: project: 'Monaco' definition: 593 buildVersionToDownload: 'latestFromBranch' - branchName: 'refs/heads/release/2024.22' + branchName: 'refs/heads/release/2025.2' targetPath: '$(Build.SourcesDirectory)/python-env-tools/bin' artifactName: 'bin-$(vsceTarget)' itemPattern: | diff --git a/package-lock.json b/package-lock.json index 01d5d5799ec7..5c677d32d68c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "python", - "version": "2025.0.0-rc", + "version": "2025.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "python", - "version": "2025.0.0-rc", + "version": "2025.2.0", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -86,7 +86,7 @@ "get-port": "^5.1.1", "gulp": "^5.0.0", "gulp-typescript": "^5.0.0", - "mocha": "^9.2.2", + "mocha": "^11.1.0", "mocha-junit-reporter": "^2.0.2", "mocha-multi-reporters": "^1.1.7", "node-has-native-dependencies": "^1.0.2", @@ -1078,6 +1078,109 @@ "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", @@ -1495,6 +1598,17 @@ "node": ">=14" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -2122,12 +2236,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/@vscode/extension-telemetry": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz", @@ -5146,6 +5254,13 @@ "node": ">=0.10.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -5162,9 +5277,9 @@ "dev": true }, "node_modules/elliptic": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", - "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dev": true, "license": "MIT", "dependencies": { @@ -7228,15 +7343,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/gulp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.0.tgz", @@ -8814,6 +8920,22 @@ "node": ">= 4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -9656,6 +9778,16 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -9676,46 +9808,39 @@ "optional": true }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha-junit-reporter": { @@ -9768,10 +9893,11 @@ } }, "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -9782,13 +9908,54 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -9799,17 +9966,12 @@ } } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -9842,6 +10004,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9879,12 +10095,13 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" @@ -9896,18 +10113,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mocha/node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9947,6 +10152,52 @@ "node": ">=8" } }, + "node_modules/mocha/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9967,26 +10218,38 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" } }, "node_modules/module-details-from-path": { @@ -10854,6 +11117,13 @@ "node": ">=8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -11005,6 +11275,30 @@ "node": ">=0.10.0" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -11919,10 +12213,11 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -12460,6 +12755,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -12540,6 +12851,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", @@ -12828,15 +13153,6 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -14333,10 +14649,11 @@ } }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -14344,17 +14661,72 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -15430,6 +15802,71 @@ "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", @@ -15769,6 +16206,13 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==" }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -16270,12 +16714,6 @@ } } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "@vscode/extension-telemetry": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz", @@ -18631,6 +19069,12 @@ } } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -18647,9 +19091,9 @@ "dev": true }, "elliptic": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", - "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "dev": true, "requires": { "bn.js": "^4.11.9", @@ -20241,12 +20685,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "gulp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.0.tgz", @@ -21387,6 +21825,16 @@ "is-object": "^1.0.1" } }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -22089,6 +22537,12 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -22106,41 +22560,37 @@ "optional": true }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "dependencies": { "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "argparse": { @@ -22149,27 +22599,50 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "ms": "^2.1.3" } }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true }, "escape-string-regexp": { @@ -22188,6 +22661,41 @@ "path-exists": "^4.0.0" } }, + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "dependencies": { + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -22213,12 +22721,12 @@ } }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "ms": { @@ -22227,12 +22735,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -22257,6 +22759,33 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -22273,19 +22802,25 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } }, @@ -23001,6 +23536,12 @@ "release-zalgo": "^1.0.0" } }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -23131,6 +23672,24 @@ "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, "path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -23824,9 +24383,9 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -24231,6 +24790,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -24290,6 +24860,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", @@ -24512,17 +25091,6 @@ "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", "terser": "^5.26.0" - }, - "dependencies": { - "serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - } } }, "test-exclude": { @@ -25663,9 +26231,9 @@ } }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { @@ -25705,6 +26273,43 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index bac34ddf767a..d0a4013a9c4b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "python", "displayName": "Python", "description": "Python language support with extension access points for IntelliSense (Pylance), Debugging (Python Debugger), linting, formatting, refactoring, unit tests, and more.", - "version": "2025.0.0-rc", + "version": "2025.2.0", "featureFlags": { "usingNewInterpreterStorage": true }, @@ -657,6 +657,12 @@ "scope": "resource", "type": "boolean" }, + "python.testing.autoTestDiscoverOnSavePattern": { + "default": "**/*.py", + "description": "%python.testing.autoTestDiscoverOnSavePattern.description%", + "scope": "resource", + "type": "string" + }, "python.testing.cwd": { "default": null, "description": "%python.testing.cwd.description%", @@ -1583,7 +1589,7 @@ "get-port": "^5.1.1", "gulp": "^5.0.0", "gulp-typescript": "^5.0.0", - "mocha": "^9.2.2", + "mocha": "^11.1.0", "mocha-junit-reporter": "^2.0.2", "mocha-multi-reporters": "^1.1.7", "node-has-native-dependencies": "^1.0.2", diff --git a/package.nls.json b/package.nls.json index d744ef430fe4..8bff60a4b07d 100644 --- a/package.nls.json +++ b/package.nls.json @@ -15,7 +15,7 @@ "python.command.python.configureTests.title": "Configure Tests", "python.command.testing.rerunFailedTests.title": "Rerun Failed Tests", "python.command.python.execSelectionInTerminal.title": "Run Selection/Line in Python Terminal", - "python.command.python.execInREPL.title": "Run Selection/Line in Python REPL", + "python.command.python.execInREPL.title": "Run Selection/Line in Native Python REPL", "python.command.python.execSelectionInDjangoShell.title": "Run Selection/Line in Django Shell", "python.command.python.reportIssue.title": "Report Issue...", "python.command.python.clearCacheAndReload.title": "Clear Cache and Reload Window", @@ -74,6 +74,7 @@ "python.terminal.focusAfterLaunch.description": "When launching a python terminal, whether to focus the cursor on the terminal.", "python.terminal.launchArgs.description": "Python launch arguments to use when executing a file in the terminal.", "python.testing.autoTestDiscoverOnSaveEnabled.description": "Enable auto run test discovery when saving a test file.", + "python.testing.autoTestDiscoverOnSavePattern.description": "Glob pattern used to determine which files are used by autoTestDiscoverOnSaveEnabled.", "python.testing.cwd.description": "Optional working directory for tests.", "python.testing.debugPort.description": "Port number used for debugging of tests.", "python.testing.promptToConfigure.description": "Prompt to configure a test framework if potential tests directories are discovered.", diff --git a/pythonExtensionApi/src/main.ts b/pythonExtensionApi/src/main.ts index 154ffbbd857a..2173245cbb28 100644 --- a/pythonExtensionApi/src/main.ts +++ b/pythonExtensionApi/src/main.ts @@ -227,9 +227,9 @@ export type EnvironmentsChangeEvent = { export type ActiveEnvironmentPathChangeEvent = EnvironmentPath & { /** - * Workspace folder the environment changed for. + * Resource the environment changed for. */ - readonly resource: WorkspaceFolder | undefined; + readonly resource: Resource | undefined; }; /** diff --git a/python_files/normalizeSelection.py b/python_files/normalizeSelection.py index 3d5137fe4aeb..9d82a4dc9440 100644 --- a/python_files/normalizeSelection.py +++ b/python_files/normalizeSelection.py @@ -120,8 +120,12 @@ def normalize_lines(selection): # Insert a newline between each top-level statement, and append a newline to the selection. source = "\n".join(statements) + "\n" + # If selection ends with trailing dictionary or list, remove last unnecessary newline. if selection[-2] == "}" or selection[-2] == "]": source = source[:-1] + # If the selection contains trailing return dictionary, insert newline to trigger execute. + if check_end_with_return_dict(selection): + source = source + "\n" except Exception: # If there's a problem when parsing statements, # append a blank line to end the block and send it as-is. @@ -134,6 +138,11 @@ def normalize_lines(selection): min_key = None +def check_end_with_return_dict(code): + stripped_code = code.strip() + return stripped_code.endswith("}") and "return {" in stripped_code.strip() + + def check_exact_exist(top_level_nodes, start_line, end_line): return [ node diff --git a/python_files/tests/test_normalize_selection.py b/python_files/tests/test_normalize_selection.py index e16eb118db12..779bb9720bfa 100644 --- a/python_files/tests/test_normalize_selection.py +++ b/python_files/tests/test_normalize_selection.py @@ -268,3 +268,50 @@ def test_list_comp(self): result = normalizeSelection.normalize_lines(src) assert result == expected + + def test_return_dict(self): + importlib.reload(normalizeSelection) + src = textwrap.dedent( + """\ + def get_dog(name, breed): + return {'name': name, 'breed': breed} + """ + ) + + expected = textwrap.dedent( + """\ + def get_dog(name, breed): + return {'name': name, 'breed': breed} + + """ + ) + + result = normalizeSelection.normalize_lines(src) + + assert result == expected + + def test_return_dict2(self): + importlib.reload(normalizeSelection) + src = textwrap.dedent( + """\ + def get_dog(name, breed): + return {'name': name, 'breed': breed} + + dog = get_dog('Ahri', 'Pomeranian') + print(dog) + """ + ) + + expected = textwrap.dedent( + """\ + def get_dog(name, breed): + return {'name': name, 'breed': breed} + + dog = get_dog('Ahri', 'Pomeranian') + print(dog) + """ + ) + + result = normalizeSelection.normalize_lines(src) + + assert result == expected diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index 0ba5fd62221a..00f356e20dcd 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -462,6 +462,11 @@ def pytest_sessionfinish(session, exitstatus): except NoSource: # as per issue 24308 this best way to handle this edge case continue + except Exception as e: + print( + f"Plugin error[vscode-pytest]: Skipping analysis of file: {file} due to error: {e}" + ) + continue lines_executable = {int(line_no) for line_no in analysis[1]} lines_missed = {int(line_no) for line_no in analysis[3]} lines_covered = lines_executable - lines_missed diff --git a/resources/report_issue_user_settings.json b/resources/report_issue_user_settings.json index ef85267c0e65..7e034651c46d 100644 --- a/resources/report_issue_user_settings.json +++ b/resources/report_issue_user_settings.json @@ -79,7 +79,8 @@ "pytestPath": "placeholder", "unittestArgs": "placeholder", "unittestEnabled": true, - "autoTestDiscoverOnSaveEnabled": true + "autoTestDiscoverOnSaveEnabled": true, + "autoTestDiscoverOnSavePattern": "placeholder" }, "terminal": { "activateEnvironment": true, diff --git a/src/client/api.ts b/src/client/api.ts index 899326647808..15fb4d688a89 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -15,7 +15,11 @@ import { IConfigurationService, Resource } from './common/types'; import { getDebugpyLauncherArgs } from './debugger/extension/adapter/remoteLaunchers'; import { IInterpreterService } from './interpreter/contracts'; import { IServiceContainer, IServiceManager } from './ioc/types'; -import { JupyterExtensionIntegration } from './jupyter/jupyterIntegration'; +import { + JupyterExtensionIntegration, + JupyterExtensionPythonEnvironments, + JupyterPythonEnvironmentApi, +} from './jupyter/jupyterIntegration'; import { traceError } from './logging'; import { IDiscoveryAPI } from './pythonEnvironments/base/locator'; import { buildEnvironmentApi } from './environmentApi'; @@ -33,11 +37,16 @@ export function buildApi( const configurationService = serviceContainer.get(IConfigurationService); const interpreterService = serviceContainer.get(IInterpreterService); serviceManager.addSingleton(JupyterExtensionIntegration, JupyterExtensionIntegration); + serviceManager.addSingleton( + JupyterExtensionPythonEnvironments, + JupyterExtensionPythonEnvironments, + ); serviceManager.addSingleton( TensorboardExtensionIntegration, TensorboardExtensionIntegration, ); const jupyterIntegration = serviceContainer.get(JupyterExtensionIntegration); + const jupyterPythonEnvApi = serviceContainer.get(JupyterExtensionPythonEnvironments); const tensorboardIntegration = serviceContainer.get( TensorboardExtensionIntegration, ); @@ -146,7 +155,7 @@ export function buildApi( stop: (client: BaseLanguageClient): Promise => client.stop(), getTelemetryReporter: () => getTelemetryReporter(), }, - environments: buildEnvironmentApi(discoveryApi, serviceContainer), + environments: buildEnvironmentApi(discoveryApi, serviceContainer, jupyterPythonEnvApi), }; // In test environment return the DI Container. diff --git a/src/client/api/types.ts b/src/client/api/types.ts index 4e67334121fb..95556aacbd90 100644 --- a/src/client/api/types.ts +++ b/src/client/api/types.ts @@ -227,9 +227,9 @@ export type EnvironmentsChangeEvent = { export type ActiveEnvironmentPathChangeEvent = EnvironmentPath & { /** - * Workspace folder the environment changed for. + * Resource the environment changed for. */ - readonly resource: WorkspaceFolder | undefined; + readonly resource: Resource | undefined; }; /** diff --git a/src/client/common/application/debugSessionTelemetry.ts b/src/client/common/application/debugSessionTelemetry.ts deleted file mode 100644 index 42b8b2651092..000000000000 --- a/src/client/common/application/debugSessionTelemetry.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -'use strict'; -import { inject, injectable } from 'inversify'; -import { DebugAdapterTracker, DebugAdapterTrackerFactory, DebugSession, ProviderResult } from 'vscode'; -import { DebugProtocol } from 'vscode-debugprotocol'; - -import { IExtensionSingleActivationService } from '../../activation/types'; -import { AttachRequestArguments, ConsoleType, LaunchRequestArguments, TriggerType } from '../../debugger/types'; -import { sendTelemetryEvent } from '../../telemetry'; -import { EventName } from '../../telemetry/constants'; -import { IDisposableRegistry } from '../types'; -import { StopWatch } from '../utils/stopWatch'; -import { IDebugService } from './types'; - -function isResponse(a: any): a is DebugProtocol.Response { - return a.type === 'response'; -} -class TelemetryTracker implements DebugAdapterTracker { - private timer = new StopWatch(); - private readonly trigger: TriggerType = 'launch'; - private readonly console: ConsoleType | undefined; - - constructor(session: DebugSession) { - this.trigger = session.configuration.request as TriggerType; - const debugConfiguration = session.configuration as Partial; - this.console = debugConfiguration.console; - } - - public onWillStartSession() { - this.sendTelemetry(EventName.DEBUG_SESSION_START); - } - - public onDidSendMessage(message: any): void { - if (isResponse(message)) { - if (message.command === 'configurationDone') { - // "configurationDone" response is sent immediately after user code starts running. - this.sendTelemetry(EventName.DEBUG_SESSION_USER_CODE_RUNNING); - } - } - } - - public onWillStopSession(): void { - this.sendTelemetry(EventName.DEBUG_SESSION_STOP); - } - - public onError?(_error: Error): void { - this.sendTelemetry(EventName.DEBUG_SESSION_ERROR); - } - - private sendTelemetry(eventName: EventName): void { - if (eventName === EventName.DEBUG_SESSION_START) { - this.timer.reset(); - } - const telemetryProps = { - trigger: this.trigger, - console: this.console, - }; - sendTelemetryEvent(eventName, this.timer.elapsedTime, telemetryProps); - } -} - -@injectable() -export class DebugSessionTelemetry implements DebugAdapterTrackerFactory, IExtensionSingleActivationService { - public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: true }; - constructor( - @inject(IDisposableRegistry) disposableRegistry: IDisposableRegistry, - @inject(IDebugService) debugService: IDebugService, - ) { - disposableRegistry.push(debugService.registerDebugAdapterTrackerFactory('python', this)); - } - - public async activate(): Promise { - // We actually register in the constructor. Not necessary to do it here - } - - public createDebugAdapterTracker(session: DebugSession): ProviderResult { - return new TelemetryTracker(session); - } -} diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index 58c41587c4f8..7ae3467b2cfd 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -320,6 +320,7 @@ export class PythonSettings implements IPythonSettings { unittestEnabled: false, pytestPath: 'pytest', autoTestDiscoverOnSaveEnabled: true, + autoTestDiscoverOnSavePattern: '**/*.py', } as ITestingSettings; } } @@ -336,6 +337,7 @@ export class PythonSettings implements IPythonSettings { unittestArgs: [], unittestEnabled: false, autoTestDiscoverOnSaveEnabled: true, + autoTestDiscoverOnSavePattern: '**/*.py', }; this.testing.pytestPath = getAbsolutePath(systemVariables.resolveAny(this.testing.pytestPath), workspaceRoot); if (this.testing.cwd) { diff --git a/src/client/common/serviceRegistry.ts b/src/client/common/serviceRegistry.ts index 5b9eb544f93b..abd2b220e400 100644 --- a/src/client/common/serviceRegistry.ts +++ b/src/client/common/serviceRegistry.ts @@ -28,7 +28,6 @@ import { CommandManager } from './application/commandManager'; import { ReloadVSCodeCommandHandler } from './application/commands/reloadCommand'; import { ReportIssueCommandHandler } from './application/commands/reportIssueCommand'; import { DebugService } from './application/debugService'; -import { DebugSessionTelemetry } from './application/debugSessionTelemetry'; import { DocumentManager } from './application/documentManager'; import { Extensions } from './application/extensions'; import { LanguageService } from './application/languageService'; @@ -189,8 +188,4 @@ export function registerTypes(serviceManager: IServiceManager): void { IExtensionSingleActivationService, ReportIssueCommandHandler, ); - serviceManager.addSingleton( - IExtensionSingleActivationService, - DebugSessionTelemetry, - ); } diff --git a/src/client/common/terminal/service.ts b/src/client/common/terminal/service.ts index b02670836015..a051d66f015f 100644 --- a/src/client/common/terminal/service.ts +++ b/src/client/common/terminal/service.ts @@ -25,6 +25,7 @@ import { useEnvExtension } from '../../envExt/api.internal'; import { ensureTerminalLegacy } from '../../envExt/api.legacy'; import { sleep } from '../utils/async'; import { isWindows } from '../utils/platform'; +import { getPythonMinorVersion } from '../../repl/replUtils'; @injectable() export class TerminalService implements ITerminalService, Disposable { @@ -108,7 +109,15 @@ export class TerminalService implements ITerminalService, Disposable { const config = getConfiguration('python'); const pythonrcSetting = config.get('terminal.shellIntegration.enabled'); - if ((isPythonShell && !pythonrcSetting) || (isPythonShell && isWindows())) { + + const minorVersion = this.options?.resource + ? await getPythonMinorVersion( + this.options.resource, + this.serviceContainer.get(IInterpreterService), + ) + : undefined; + + if ((isPythonShell && !pythonrcSetting) || (isPythonShell && isWindows()) || (minorVersion ?? 0) >= 13) { // If user has explicitly disabled SI for Python, use sendText for inside Terminal REPL. terminal.sendText(commandLine); return undefined; diff --git a/src/client/debugger/extension/adapter/factory.ts b/src/client/debugger/extension/adapter/factory.ts index 546414699971..edef16368dc0 100644 --- a/src/client/debugger/extension/adapter/factory.ts +++ b/src/client/debugger/extension/adapter/factory.ts @@ -17,8 +17,6 @@ import { EXTENSION_ROOT_DIR } from '../../../constants'; import { IInterpreterService } from '../../../interpreter/contracts'; import { traceError, traceLog, traceVerbose } from '../../../logging'; import { PythonEnvironment } from '../../../pythonEnvironments/info'; -import { sendTelemetryEvent } from '../../../telemetry'; -import { EventName } from '../../../telemetry/constants'; import { AttachRequestArguments, LaunchRequestArguments } from '../../types'; import { IDebugAdapterDescriptorFactory } from '../types'; import { showErrorMessage } from '../../../common/vscodeApis/windowApis'; @@ -76,10 +74,6 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac const command = await this.getDebugAdapterPython(configuration, session.workspaceFolder); if (command.length !== 0) { - if (configuration.request === 'attach' && configuration.processId !== undefined) { - sendTelemetryEvent(EventName.DEBUGGER_ATTACH_TO_LOCAL_PROCESS); - } - const executable = command.shift() ?? 'python'; // "logToFile" is not handled directly by the adapter - instead, we need to pass @@ -100,7 +94,6 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac const args = command.concat([debuggerAdapterPathToUse, ...logArgs]); traceLog(`DAP Server launched with command: ${executable} ${args.join(' ')}`); - sendTelemetryEvent(EventName.DEBUG_ADAPTER_USING_WHEELS_PATH, undefined, { usingWheels: true }); return new DebugAdapterExecutable(executable, args); } diff --git a/src/client/debugger/extension/configuration/resolvers/attach.ts b/src/client/debugger/extension/configuration/resolvers/attach.ts index 6e5ca501463e..1c232f261d03 100644 --- a/src/client/debugger/extension/configuration/resolvers/attach.ts +++ b/src/client/debugger/extension/configuration/resolvers/attach.ts @@ -87,7 +87,6 @@ export class AttachConfigurationResolver extends BaseConfigurationResolver ): boolean { return !!(debugConfiguration.module && debugConfiguration.module.toUpperCase() === 'FLASK'); } - - protected static sendTelemetry( - trigger: 'launch' | 'attach' | 'test', - debugConfiguration: Partial, - ): void { - const name = debugConfiguration.name || ''; - const moduleName = debugConfiguration.module || ''; - const telemetryProps: DebuggerTelemetry = { - trigger, - console: debugConfiguration.console, - hasEnvVars: typeof debugConfiguration.env === 'object' && Object.keys(debugConfiguration.env).length > 0, - django: !!debugConfiguration.django, - fastapi: BaseConfigurationResolver.isDebuggingFastAPI(debugConfiguration), - flask: BaseConfigurationResolver.isDebuggingFlask(debugConfiguration), - hasArgs: Array.isArray(debugConfiguration.args) && debugConfiguration.args.length > 0, - isLocalhost: BaseConfigurationResolver.isLocalHost(debugConfiguration.host), - isModule: moduleName.length > 0, - isSudo: !!debugConfiguration.sudo, - jinja: !!debugConfiguration.jinja, - pyramid: !!debugConfiguration.pyramid, - stopOnEntry: !!debugConfiguration.stopOnEntry, - showReturnValue: !!debugConfiguration.showReturnValue, - subProcess: !!debugConfiguration.subProcess, - watson: name.toLowerCase().indexOf('watson') >= 0, - pyspark: name.toLowerCase().indexOf('pyspark') >= 0, - gevent: name.toLowerCase().indexOf('gevent') >= 0, - scrapy: moduleName.toLowerCase() === 'scrapy', - }; - sendTelemetryEvent(EventName.DEBUGGER, undefined, telemetryProps); - } } diff --git a/src/client/debugger/extension/configuration/resolvers/launch.ts b/src/client/debugger/extension/configuration/resolvers/launch.ts index d9bedaa5e4d5..3ca38fb0f710 100644 --- a/src/client/debugger/extension/configuration/resolvers/launch.ts +++ b/src/client/debugger/extension/configuration/resolvers/launch.ts @@ -13,7 +13,7 @@ import { EnvironmentVariables } from '../../../../common/variables/types'; import { IEnvironmentActivationService } from '../../../../interpreter/activation/types'; import { IInterpreterService } from '../../../../interpreter/contracts'; import { DebuggerTypeName } from '../../../constants'; -import { DebugOptions, DebugPurpose, LaunchRequestArguments } from '../../../types'; +import { DebugOptions, LaunchRequestArguments } from '../../../types'; import { BaseConfigurationResolver } from './base'; import { getProgram, IDebugEnvironmentVariablesService } from './helper'; import { @@ -194,11 +194,6 @@ export class LaunchConfigurationResolver extends BaseConfigurationResolver 0 ? pathMappings : undefined; } - const trigger = - debugConfiguration.purpose?.includes(DebugPurpose.DebugTest) || debugConfiguration.request === 'test' - ? 'test' - : 'launch'; - LaunchConfigurationResolver.sendTelemetry(trigger, debugConfiguration); } protected async validateLaunchConfiguration( diff --git a/src/client/debugger/extension/debugCommands.ts b/src/client/debugger/extension/debugCommands.ts index b3322e8e7dd1..629f8616a6d6 100644 --- a/src/client/debugger/extension/debugCommands.ts +++ b/src/client/debugger/extension/debugCommands.ts @@ -33,7 +33,6 @@ export class DebugCommands implements IExtensionSingleActivationService { public activate(): Promise { this.disposables.push( this.commandManager.registerCommand(Commands.Debug_In_Terminal, async (file?: Uri) => { - sendTelemetryEvent(EventName.DEBUG_IN_TERMINAL_BUTTON); const interpreter = await this.interpreterService.getActiveInterpreter(file); if (!interpreter) { this.commandManager.executeCommand(Commands.TriggerEnvironmentSelection, file).then(noop, noop); diff --git a/src/client/debugger/extension/hooks/childProcessAttachService.ts b/src/client/debugger/extension/hooks/childProcessAttachService.ts index 24eaf1b52769..39556f94c87c 100644 --- a/src/client/debugger/extension/hooks/childProcessAttachService.ts +++ b/src/client/debugger/extension/hooks/childProcessAttachService.ts @@ -7,8 +7,6 @@ import { inject, injectable } from 'inversify'; import { IDebugService } from '../../../common/application/types'; import { DebugConfiguration, DebugSession, l10n, WorkspaceFolder, DebugSessionOptions } from 'vscode'; import { noop } from '../../../common/utils/misc'; -import { captureTelemetry } from '../../../telemetry'; -import { EventName } from '../../../telemetry/constants'; import { AttachRequestArguments } from '../../types'; import { IChildProcessAttachService } from './types'; import { showErrorMessage } from '../../../common/vscodeApis/windowApis'; @@ -22,7 +20,6 @@ import { getWorkspaceFolders } from '../../../common/vscodeApis/workspaceApis'; export class ChildProcessAttachService implements IChildProcessAttachService { constructor(@inject(IDebugService) private readonly debugService: IDebugService) {} - @captureTelemetry(EventName.DEBUGGER_ATTACH_TO_CHILD_PROCESS) public async attach(data: AttachRequestArguments & DebugConfiguration, parentSession: DebugSession): Promise { const debugConfig: AttachRequestArguments & DebugConfiguration = data; const folder = this.getRelatedWorkspaceFolder(debugConfig); diff --git a/src/client/envExt/api.internal.ts b/src/client/envExt/api.internal.ts index a47193d3cd95..7d511eac49ea 100644 --- a/src/client/envExt/api.internal.ts +++ b/src/client/envExt/api.internal.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Terminal, Uri } from 'vscode'; +import { EventEmitter, Terminal, Uri, Disposable, ConfigurationTarget } from 'vscode'; import { getExtension } from '../common/vscodeApis/extensionsApi'; import { GetEnvironmentScope, @@ -10,8 +10,10 @@ import { PythonEnvironmentApi, PythonProcess, RefreshEnvironmentsScope, + DidChangeEnvironmentEventArgs, } from './types'; import { executeCommand } from '../common/vscodeApis/commandApis'; +import { IInterpreterPathService } from '../common/types'; export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs'; @@ -24,6 +26,17 @@ export function useEnvExtension(): boolean { return _useExt; } +const onDidChangeEnvironmentEnvExtEmitter: EventEmitter = new EventEmitter< + DidChangeEnvironmentEventArgs +>(); +export function onDidChangeEnvironmentEnvExt( + listener: (e: DidChangeEnvironmentEventArgs) => unknown, + thisArgs?: unknown, + disposables?: Disposable[], +): Disposable { + return onDidChangeEnvironmentEnvExtEmitter.event(listener, thisArgs, disposables); +} + let _extApi: PythonEnvironmentApi | undefined; export async function getEnvExtApi(): Promise { if (_extApi) { @@ -33,14 +46,15 @@ export async function getEnvExtApi(): Promise { if (!extension) { throw new Error('Python Environments extension not found.'); } - if (extension?.isActive) { - _extApi = extension.exports as PythonEnvironmentApi; - return _extApi; + if (!extension?.isActive) { + await extension.activate(); } - await extension.activate(); - _extApi = extension.exports as PythonEnvironmentApi; + _extApi.onDidChangeEnvironment((e) => { + onDidChangeEnvironmentEnvExtEmitter.fire(e); + }); + return _extApi; } @@ -106,3 +120,32 @@ export async function clearCache(): Promise { await executeCommand('python-envs.clearCache'); } } + +export function registerEnvExtFeatures( + disposables: Disposable[], + interpreterPathService: IInterpreterPathService, +): void { + if (useEnvExtension()) { + disposables.push( + onDidChangeEnvironmentEnvExt(async (e: DidChangeEnvironmentEventArgs) => { + const previousPath = interpreterPathService.get(e.uri); + + if (previousPath !== e.new?.environmentPath.fsPath) { + if (e.uri) { + await interpreterPathService.update( + e.uri, + ConfigurationTarget.WorkspaceFolder, + e.new?.environmentPath.fsPath, + ); + } else { + await interpreterPathService.update( + undefined, + ConfigurationTarget.Global, + e.new?.environmentPath.fsPath, + ); + } + } + }), + ); + } +} diff --git a/src/client/envExt/api.legacy.ts b/src/client/envExt/api.legacy.ts index 1d9d94ccc98f..7546a429c76a 100644 --- a/src/client/envExt/api.legacy.ts +++ b/src/client/envExt/api.legacy.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { Terminal, Uri } from 'vscode'; +import { Terminal, Uri, WorkspaceFolder } from 'vscode'; import { getEnvExtApi, getEnvironment } from './api.internal'; import { EnvironmentType, PythonEnvironment as PythonEnvironmentLegacy } from '../pythonEnvironments/info'; import { PythonEnvironment, PythonTerminalOptions } from './types'; @@ -10,7 +10,7 @@ import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; import { PythonEnvType } from '../pythonEnvironments/base/info'; import { traceError, traceInfo } from '../logging'; import { reportActiveInterpreterChanged } from '../environmentApi'; -import { getWorkspaceFolder } from '../common/vscodeApis/workspaceApis'; +import { getWorkspaceFolder, getWorkspaceFolders } from '../common/vscodeApis/workspaceApis'; function toEnvironmentType(pythonEnv: PythonEnvironment): EnvironmentType { if (pythonEnv.envId.managerId.toLowerCase().endsWith('system')) { @@ -106,16 +106,25 @@ export async function getActiveInterpreterLegacy(resource?: Uri): Promise 0 && resource !== undefined); + if (shouldReport && newEnv && oldEnv?.envId.id !== pythonEnv?.envId.id) { reportActiveInterpreterChanged({ resource: getWorkspaceFolder(resource), path: newEnv.path, }); + previousEnvMap.set(uri?.fsPath || '', pythonEnv); } return pythonEnv ? toLegacyType(pythonEnv) : undefined; } -export async function ensureEnvironmentContainsPythonLegacy(pythonPath: string): Promise { +export async function ensureEnvironmentContainsPythonLegacy( + pythonPath: string, + workspaceFolder: WorkspaceFolder | undefined, + callback: () => void, +): Promise { const api = await getEnvExtApi(); const pythonEnv = await api.resolveEnvironment(Uri.file(pythonPath)); if (!pythonEnv) { @@ -132,6 +141,12 @@ export async function ensureEnvironmentContainsPythonLegacy(pythonPath: string): traceInfo(`EnvExt: Python not found in ${envType} environment ${pythonPath}`); traceInfo(`EnvExt: Installing Python in ${envType} environment ${pythonPath}`); await api.installPackages(pythonEnv, ['python']); + previousEnvMap.set(workspaceFolder?.uri.fsPath || '', pythonEnv); + reportActiveInterpreterChanged({ + path: pythonPath, + resource: workspaceFolder, + }); + callback(); } } diff --git a/src/client/environmentApi.ts b/src/client/environmentApi.ts index 6c4b5cf94d92..ecd8eef21845 100644 --- a/src/client/environmentApi.ts +++ b/src/client/environmentApi.ts @@ -11,7 +11,7 @@ import { PythonEnvInfo, PythonEnvKind, PythonEnvType } from './pythonEnvironment import { getEnvPath } from './pythonEnvironments/base/info/env'; import { IDiscoveryAPI, ProgressReportStage } from './pythonEnvironments/base/locator'; import { IPythonExecutionFactory } from './common/process/types'; -import { traceError, traceVerbose } from './logging'; +import { traceError, traceInfo, traceVerbose } from './logging'; import { isParentPath, normCasePath } from './common/platform/fs-paths'; import { sendTelemetryEvent } from './telemetry'; import { EventName } from './telemetry/constants'; @@ -33,6 +33,8 @@ import { } from './api/types'; import { buildEnvironmentCreationApi } from './pythonEnvironments/creation/createEnvApi'; import { EnvironmentKnownCache } from './environmentKnownCache'; +import type { JupyterPythonEnvironmentApi } from './jupyter/jupyterIntegration'; +import { noop } from './common/utils/misc'; type ActiveEnvironmentChangeEvent = { resource: WorkspaceFolder | undefined; @@ -40,7 +42,13 @@ type ActiveEnvironmentChangeEvent = { }; const onDidActiveInterpreterChangedEvent = new EventEmitter(); +const previousEnvMap = new Map(); export function reportActiveInterpreterChanged(e: ActiveEnvironmentChangeEvent): void { + const oldPath = previousEnvMap.get(e.resource?.uri.fsPath ?? ''); + if (oldPath === e.path) { + return; + } + previousEnvMap.set(e.resource?.uri.fsPath ?? '', e.path); onDidActiveInterpreterChangedEvent.fire({ id: getEnvID(e.path), path: e.path, resource: e.resource }); reportActiveInterpreterChangedDeprecated({ path: e.path, resource: e.resource?.uri }); } @@ -115,6 +123,7 @@ function filterUsingVSCodeContext(e: PythonEnvInfo) { export function buildEnvironmentApi( discoveryApi: IDiscoveryAPI, serviceContainer: IServiceContainer, + jupyterPythonEnvsApi: JupyterPythonEnvironmentApi, ): PythonExtension['environments'] { const interpreterPathService = serviceContainer.get(IInterpreterPathService); const configService = serviceContainer.get(IConfigurationService); @@ -146,7 +155,36 @@ export function buildEnvironmentApi( }) .ignoreErrors(); } + + function getActiveEnvironmentPath(resource?: Resource) { + resource = resource && 'uri' in resource ? resource.uri : resource; + const jupyterEnv = + resource && jupyterPythonEnvsApi.getPythonEnvironment + ? jupyterPythonEnvsApi.getPythonEnvironment(resource) + : undefined; + if (jupyterEnv) { + traceVerbose('Python Environment returned from Jupyter', resource?.fsPath, jupyterEnv.id); + return { + id: jupyterEnv.id, + path: jupyterEnv.path, + }; + } + const path = configService.getSettings(resource).pythonPath; + const id = path === 'python' ? 'DEFAULT_PYTHON' : getEnvID(path); + return { + id, + path, + }; + } + disposables.push( + onDidActiveInterpreterChangedEvent.event((e) => { + let scope = 'global'; + if (e.resource) { + scope = e.resource instanceof Uri ? e.resource.fsPath : e.resource.uri.fsPath; + } + traceInfo(`Active interpreter [${scope}]: `, e.path); + }), discoveryApi.onProgress((e) => { if (e.stage === ProgressReportStage.discoveryFinished) { knownCache = initKnownCache(); @@ -206,6 +244,16 @@ export function buildEnvironmentApi( }), onEnvironmentsChanged, onEnvironmentVariablesChanged, + jupyterPythonEnvsApi.onDidChangePythonEnvironment + ? jupyterPythonEnvsApi.onDidChangePythonEnvironment((e) => { + const jupyterEnv = getActiveEnvironmentPath(e); + onDidActiveInterpreterChangedEvent.fire({ + id: jupyterEnv.id, + path: jupyterEnv.path, + resource: e, + }); + }, undefined) + : { dispose: noop }, ); if (!knownCache!) { knownCache = initKnownCache(); @@ -223,13 +271,7 @@ export function buildEnvironmentApi( }, getActiveEnvironmentPath(resource?: Resource) { sendApiTelemetry('getActiveEnvironmentPath'); - resource = resource && 'uri' in resource ? resource.uri : resource; - const path = configService.getSettings(resource).pythonPath; - const id = path === 'python' ? 'DEFAULT_PYTHON' : getEnvID(path); - return { - id, - path, - }; + return getActiveEnvironmentPath(resource); }, updateActiveEnvironmentPath(env: Environment | EnvironmentPath | string, resource?: Resource): Promise { sendApiTelemetry('updateActiveEnvironmentPath'); diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 4a1acca62da5..362fcf8468ad 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -56,6 +56,7 @@ import { registerTriggerForTerminalREPL } from './terminals/codeExecution/termin import { registerPythonStartup } from './terminals/pythonStartup'; import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi'; import { registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider'; +import { registerEnvExtFeatures } from './envExt/api.internal'; export async function activateComponents( // `ext` is passed to any extra activation funcs. @@ -101,6 +102,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components): const interpreterService: IInterpreterService = ext.legacyIOC.serviceContainer.get( IInterpreterService, ); + registerEnvExtFeatures(ext.disposables, interpreterPathService); const pathUtils = ext.legacyIOC.serviceContainer.get(IPathUtils); registerPixiFeatures(ext.disposables); registerAllCreateEnvironmentFeatures( diff --git a/src/client/interpreter/interpreterService.ts b/src/client/interpreter/interpreterService.ts index 628a25d6b3b1..3a1aaed312ff 100644 --- a/src/client/interpreter/interpreterService.ts +++ b/src/client/interpreter/interpreterService.ts @@ -290,11 +290,8 @@ export class InterpreterService implements Disposable, IInterpreterService { @cache(-1, true) private async ensureEnvironmentContainsPython(pythonPath: string, workspaceFolder: WorkspaceFolder | undefined) { if (useEnvExtension()) { - await ensureEnvironmentContainsPythonLegacy(pythonPath); - this.didChangeInterpreterEmitter.fire(workspaceFolder?.uri); - reportActiveInterpreterChanged({ - path: pythonPath, - resource: workspaceFolder, + await ensureEnvironmentContainsPythonLegacy(pythonPath, workspaceFolder, () => { + this.didChangeInterpreterEmitter.fire(workspaceFolder?.uri); }); return; } diff --git a/src/client/jupyter/jupyterIntegration.ts b/src/client/jupyter/jupyterIntegration.ts index 69583b744da9..1136502c1ef2 100644 --- a/src/client/jupyter/jupyterIntegration.ts +++ b/src/client/jupyter/jupyterIntegration.ts @@ -1,12 +1,12 @@ /* eslint-disable comma-dangle */ -/* eslint-disable implicit-arrow-linebreak */ +/* eslint-disable implicit-arrow-linebreak, max-classes-per-file */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { inject, injectable, named } from 'inversify'; import { dirname } from 'path'; -import { Extension, Memento, Uri } from 'vscode'; +import { EventEmitter, Extension, Memento, Uri, workspace, Event } from 'vscode'; import type { SemVer } from 'semver'; import { IContextKeyManager, IWorkspaceService } from '../common/application/types'; import { JUPYTER_EXTENSION_ID, PYLANCE_EXTENSION_ID } from '../common/constants'; @@ -23,6 +23,7 @@ import { PylanceApi } from '../activation/node/pylanceApi'; import { ExtensionContextKey } from '../common/application/contextKeys'; import { getDebugpyPath } from '../debugger/pythonDebugger'; import type { Environment } from '../api/types'; +import { DisposableBase } from '../common/utils/resourceLifecycle'; type PythonApiForJupyterExtension = { /** @@ -170,3 +171,108 @@ export class JupyterExtensionIntegration { } } } + +export interface JupyterPythonEnvironmentApi { + /** + * This event is triggered when the environment associated with a Jupyter Notebook or Interactive Window changes. + * The Uri in the event is the Uri of the Notebook/IW. + */ + onDidChangePythonEnvironment?: Event; + /** + * Returns the EnvironmentPath to the Python environment associated with a Jupyter Notebook or Interactive Window. + * If the Uri is not associated with a Jupyter Notebook or Interactive Window, then this method returns undefined. + * @param uri + */ + getPythonEnvironment?( + uri: Uri, + ): + | undefined + | { + /** + * The ID of the environment. + */ + readonly id: string; + /** + * Path to environment folder or path to python executable that uniquely identifies an environment. Environments + * lacking a python executable are identified by environment folder paths, whereas other envs can be identified + * using python executable path. + */ + readonly path: string; + }; +} + +@injectable() +export class JupyterExtensionPythonEnvironments extends DisposableBase implements JupyterPythonEnvironmentApi { + private jupyterExtension?: JupyterPythonEnvironmentApi; + + private readonly _onDidChangePythonEnvironment = this._register(new EventEmitter()); + + public readonly onDidChangePythonEnvironment = this._onDidChangePythonEnvironment.event; + + constructor(@inject(IExtensions) private readonly extensions: IExtensions) { + super(); + } + + public getPythonEnvironment( + uri: Uri, + ): + | undefined + | { + /** + * The ID of the environment. + */ + readonly id: string; + /** + * Path to environment folder or path to python executable that uniquely identifies an environment. Environments + * lacking a python executable are identified by environment folder paths, whereas other envs can be identified + * using python executable path. + */ + readonly path: string; + } { + if (!isJupyterResource(uri)) { + return undefined; + } + const api = this.getJupyterApi(); + if (api?.getPythonEnvironment) { + return api.getPythonEnvironment(uri); + } + return undefined; + } + + private getJupyterApi() { + if (!this.jupyterExtension) { + const ext = this.extensions.getExtension(JUPYTER_EXTENSION_ID); + if (!ext) { + return undefined; + } + if (!ext.isActive) { + ext.activate().then(() => { + this.hookupOnDidChangePythonEnvironment(ext.exports); + }); + return undefined; + } + this.hookupOnDidChangePythonEnvironment(ext.exports); + } + return this.jupyterExtension; + } + + private hookupOnDidChangePythonEnvironment(api: JupyterPythonEnvironmentApi) { + this.jupyterExtension = api; + if (api.onDidChangePythonEnvironment) { + this._register( + api.onDidChangePythonEnvironment( + this._onDidChangePythonEnvironment.fire, + this._onDidChangePythonEnvironment, + ), + ); + } + } +} + +function isJupyterResource(resource: Uri): boolean { + // Jupyter extension only deals with Notebooks and Interactive Windows. + return ( + resource.fsPath.endsWith('.ipynb') || + workspace.notebookDocuments.some((item) => item.uri.toString() === resource.toString()) + ); +} diff --git a/src/client/pythonEnvironments/common/environmentManagers/conda.ts b/src/client/pythonEnvironments/common/environmentManagers/conda.ts index 5301f82eda18..2fd3f3207fc5 100644 --- a/src/client/pythonEnvironments/common/environmentManagers/conda.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/conda.ts @@ -564,11 +564,8 @@ export class Conda { return undefined; } const args = []; - if (env.name) { - args.push('-n', env.name); - } else { - args.push('-p', env.prefix); - } + args.push('-p', env.prefix); + const python = [ forShellExecution ? this.shellCommand : this.command, 'run', diff --git a/src/client/repl/replUtils.ts b/src/client/repl/replUtils.ts index 0c2c4ba0d84e..8e23218c2870 100644 --- a/src/client/repl/replUtils.ts +++ b/src/client/repl/replUtils.ts @@ -118,3 +118,17 @@ export function getTabNameForUri(uri: Uri): string | undefined { return undefined; } + +/** + * Function that will return the minor version of current active Python interpreter. + */ +export async function getPythonMinorVersion( + uri: Uri | undefined, + interpreterService: IInterpreterService, +): Promise { + if (uri) { + const pythonVersion = await getActiveInterpreter(uri, interpreterService); + return pythonVersion?.version?.minor; + } + return undefined; +} diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index 53420c275e8a..ecc44177338a 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -31,7 +31,6 @@ export enum EventName { PYTHON_INTERPRETER_ACTIVATE_ENVIRONMENT_PROMPT = 'PYTHON_INTERPRETER_ACTIVATE_ENVIRONMENT_PROMPT', PYTHON_NOT_INSTALLED_PROMPT = 'PYTHON_NOT_INSTALLED_PROMPT', CONDA_INHERIT_ENV_PROMPT = 'CONDA_INHERIT_ENV_PROMPT', - TERMINAL_DEACTIVATE_PROMPT = 'TERMINAL_DEACTIVATE_PROMPT', REQUIRE_JUPYTER_PROMPT = 'REQUIRE_JUPYTER_PROMPT', ACTIVATED_CONDA_ENV_LAUNCH = 'ACTIVATED_CONDA_ENV_LAUNCH', ENVFILE_VARIABLE_SUBSTITUTION = 'ENVFILE_VARIABLE_SUBSTITUTION', @@ -39,16 +38,6 @@ export enum EventName { EXECUTION_CODE = 'EXECUTION_CODE', EXECUTION_DJANGO = 'EXECUTION_DJANGO', - DEBUG_IN_TERMINAL_BUTTON = 'DEBUG.IN_TERMINAL', - DEBUG_ADAPTER_USING_WHEELS_PATH = 'DEBUG_ADAPTER.USING_WHEELS_PATH', - DEBUG_SESSION_ERROR = 'DEBUG_SESSION.ERROR', - DEBUG_SESSION_START = 'DEBUG_SESSION.START', - DEBUG_SESSION_STOP = 'DEBUG_SESSION.STOP', - DEBUG_SESSION_USER_CODE_RUNNING = 'DEBUG_SESSION.USER_CODE_RUNNING', - DEBUGGER = 'DEBUGGER', - DEBUGGER_ATTACH_TO_CHILD_PROCESS = 'DEBUGGER.ATTACH_TO_CHILD_PROCESS', - DEBUGGER_ATTACH_TO_LOCAL_PROCESS = 'DEBUGGER.ATTACH_TO_LOCAL_PROCESS', - // Python testing specific telemetry UNITTEST_CONFIGURING = 'UNITTEST.CONFIGURING', UNITTEST_CONFIGURE = 'UNITTEST.CONFIGURE', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 37ae9328c546..6c97bd083d96 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -9,7 +9,6 @@ import { AppinsightsKey, isTestExecution, isUnitTestExecution, PVSC_EXTENSION_ID import type { TerminalShellType } from '../common/terminal/types'; import { isPromise } from '../common/utils/async'; import { StopWatch } from '../common/utils/stopWatch'; -import { ConsoleType, TriggerType } from '../debugger/types'; import { EnvironmentType, PythonEnvironment } from '../pythonEnvironments/info'; import { TensorBoardPromptSelection } from '../tensorBoard/constants'; import { EventName } from './constants'; @@ -300,327 +299,6 @@ type FailedEventType = { failed: true }; // Map all events to their properties export interface IEventNamePropertyMapping { - /** - * Telemetry event sent when debug in terminal button was used to debug current file. - */ - /* __GDPR__ - "debug_in_terminal_button" : { "owner": "paulacamargo25" } - */ - [EventName.DEBUG_IN_TERMINAL_BUTTON]: never | undefined; - /** - * Telemetry event captured when debug adapter executable is created - */ - /* __GDPR__ - "debug_adapter.using_wheels_path" : { - "usingwheels" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" } - } - */ - - [EventName.DEBUG_ADAPTER_USING_WHEELS_PATH]: { - /** - * Carries boolean - * - `true` if path used for the adapter is the debugger with wheels. - * - `false` if path used for the adapter is the source only version of the debugger. - */ - usingWheels: boolean; - }; - /** - * Telemetry captured before starting debug session. - */ - /* __GDPR__ - "debug_session.start" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "trigger" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" }, - "console" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" } - } - */ - [EventName.DEBUG_SESSION_START]: { - /** - * Trigger for starting the debugger. - * - `launch`: Launch/start new code and debug it. - * - `attach`: Attach to an exiting python process (remote debugging). - * - `test`: Debugging python tests. - * - * @type {TriggerType} - */ - trigger: TriggerType; - /** - * Type of console used. - * -`internalConsole`: Use VS Code debug console (no shells/terminals). - * - `integratedTerminal`: Use VS Code terminal. - * - `externalTerminal`: Use an External terminal. - * - * @type {ConsoleType} - */ - console?: ConsoleType; - }; - /** - * Telemetry captured when debug session runs into an error. - */ - /* __GDPR__ - "debug_session.error" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "trigger" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" }, - "console" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" } - } - */ - [EventName.DEBUG_SESSION_ERROR]: { - /** - * Trigger for starting the debugger. - * - `launch`: Launch/start new code and debug it. - * - `attach`: Attach to an exiting python process (remote debugging). - * - `test`: Debugging python tests. - * - * @type {TriggerType} - */ - trigger: TriggerType; - /** - * Type of console used. - * -`internalConsole`: Use VS Code debug console (no shells/terminals). - * - `integratedTerminal`: Use VS Code terminal. - * - `externalTerminal`: Use an External terminal. - * - * @type {ConsoleType} - */ - console?: ConsoleType; - }; - /** - * Telemetry captured after stopping debug session. - */ - /* __GDPR__ - "debug_session.stop" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "trigger" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" }, - "console" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" } - } - */ - [EventName.DEBUG_SESSION_STOP]: { - /** - * Trigger for starting the debugger. - * - `launch`: Launch/start new code and debug it. - * - `attach`: Attach to an exiting python process (remote debugging). - * - `test`: Debugging python tests. - * - * @type {TriggerType} - */ - trigger: TriggerType; - /** - * Type of console used. - * -`internalConsole`: Use VS Code debug console (no shells/terminals). - * - `integratedTerminal`: Use VS Code terminal. - * - `externalTerminal`: Use an External terminal. - * - * @type {ConsoleType} - */ - console?: ConsoleType; - }; - /** - * Telemetry captured when user code starts running after loading the debugger. - */ - /* __GDPR__ - "debug_session.user_code_running" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "trigger" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" }, - "console" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "paulacamargo25" } - } - */ - [EventName.DEBUG_SESSION_USER_CODE_RUNNING]: { - /** - * Trigger for starting the debugger. - * - `launch`: Launch/start new code and debug it. - * - `attach`: Attach to an exiting python process (remote debugging). - * - `test`: Debugging python tests. - * - * @type {TriggerType} - */ - trigger: TriggerType; - /** - * Type of console used. - * -`internalConsole`: Use VS Code debug console (no shells/terminals). - * - `integratedTerminal`: Use VS Code terminal. - * - `externalTerminal`: Use an External terminal. - * - * @type {ConsoleType} - */ - console?: ConsoleType; - }; - /** - * Telemetry captured when starting the debugger. - */ - /* __GDPR__ - "debugger" : { - "trigger" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "console" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "hasenvvars": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "hasargs": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "django": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "fastapi": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "flask": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "jinja": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "islocalhost": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "ismodule": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "issudo": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "stoponentry": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "showreturnvalue": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "pyramid": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "subprocess": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "watson": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "pyspark": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "gevent": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" }, - "scrapy": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "paulacamargo25" } - } - */ - [EventName.DEBUGGER]: { - /** - * Trigger for starting the debugger. - * - `launch`: Launch/start new code and debug it. - * - `attach`: Attach to an exiting python process (remote debugging). - * - `test`: Debugging python tests. - * - * @type {TriggerType} - */ - trigger: TriggerType; - /** - * Type of console used. - * -`internalConsole`: Use VS Code debug console (no shells/terminals). - * - `integratedTerminal`: Use VS Code terminal. - * - `externalTerminal`: Use an External terminal. - * - * @type {ConsoleType} - */ - console?: ConsoleType; - /** - * Whether user has defined environment variables. - * Could have been defined in launch.json or the env file (defined in `settings.json`). - * Default `env file` is `.env` in the workspace folder. - * - * @type {boolean} - */ - hasEnvVars: boolean; - /** - * Whether there are any CLI arguments that need to be passed into the program being debugged. - * - * @type {boolean} - */ - hasArgs: boolean; - /** - * Whether the user is debugging `django`. - * - * @type {boolean} - */ - django: boolean; - /** - * Whether the user is debugging `fastapi`. - * - * @type {boolean} - */ - fastapi: boolean; - /** - * Whether the user is debugging `flask`. - * - * @type {boolean} - */ - flask: boolean; - /** - * Whether the user is debugging `jinja` templates. - * - * @type {boolean} - */ - jinja: boolean; - /** - * Whether user is attaching to a local python program (attach scenario). - * - * @type {boolean} - */ - isLocalhost: boolean; - /** - * Whether debugging a module. - * - * @type {boolean} - */ - isModule: boolean; - /** - * Whether debugging with `sudo`. - * - * @type {boolean} - */ - isSudo: boolean; - /** - * Whether required to stop upon entry. - * - * @type {boolean} - */ - stopOnEntry: boolean; - /** - * Whether required to display return types in debugger. - * - * @type {boolean} - */ - showReturnValue: boolean; - /** - * Whether debugging `pyramid`. - * - * @type {boolean} - */ - pyramid: boolean; - /** - * Whether debugging a subprocess. - * - * @type {boolean} - */ - subProcess: boolean; - /** - * Whether debugging `watson`. - * - * @type {boolean} - */ - watson: boolean; - /** - * Whether degbugging `pyspark`. - * - * @type {boolean} - */ - pyspark: boolean; - /** - * Whether using `gevent` when debugging. - * - * @type {boolean} - */ - gevent: boolean; - /** - * Whether debugging `scrapy`. - * - * @type {boolean} - */ - scrapy: boolean; - }; - /** - * Telemetry event sent when attaching to child process - */ - /* __GDPR__ - "debugger.attach_to_child_process" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "paulacamargo25" } - } - */ - [EventName.DEBUGGER_ATTACH_TO_CHILD_PROCESS]: never | undefined; - /** - * Telemetry event sent when attaching to a local process. - */ - /* __GDPR__ - "debugger.attach_to_local_process" : { "owner": "paulacamargo25" } - */ - [EventName.DEBUGGER_ATTACH_TO_LOCAL_PROCESS]: never | undefined; - /** - * Telemetry event sent with details of actions when invoking a diagnostic command - */ - /* __GDPR__ - "diagnostics.action" : { - "commandname" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "ignorecode" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "action" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" } - } - */ [EventName.DIAGNOSTICS_ACTION]: { /** * Diagnostics command executed. @@ -2021,23 +1699,7 @@ export interface IEventNamePropertyMapping { */ selection: 'Allow' | 'Close' | undefined; }; - /** - * Telemetry event sent with details when user clicks the prompt with the following message: - * - * 'We noticed you're using a conda environment. If you are experiencing issues with this environment in the integrated terminal, we suggest the "terminal.integrated.inheritEnv" setting to be changed to false. Would you like to update this setting?' - */ - /* __GDPR__ - "conda_inherit_env_prompt" : { - "selection" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" } - } - */ - [EventName.TERMINAL_DEACTIVATE_PROMPT]: { - /** - * `Yes` When 'Allow' option is selected - * `Close` When 'Close' option is selected - */ - selection: 'Edit script' | "Don't show again" | undefined; - }; + /** * Telemetry event sent with details when user attempts to run in interactive window when Jupyter is not installed. */ @@ -2310,8 +1972,13 @@ export interface IEventNamePropertyMapping { [EventName.REPL]: { /** * Whether the user launched the Terminal REPL or Native REPL + * + * Terminal - Terminal REPL user ran `Python: Start Terminal REPL` command. + * Native - Native REPL user ran `Python: Start Native Python REPL` command. + * manualTerminal - User started REPL in terminal using `python`, `python3` or `py` etc without arguments in terminal. + * runningScript - User ran a script in terminal like `python myscript.py`. */ - replType: 'Terminal' | 'Native' | 'manualTerminal'; + replType: 'Terminal' | 'Native' | 'manualTerminal' | `runningScript`; }; /** * Telemetry event sent if and when user configure tests command. This command can be trigerred from multiple places in the extension. (Command palette, prompt etc.) diff --git a/src/client/telemetry/pylance.ts b/src/client/telemetry/pylance.ts index 778bfc97be12..42d177488790 100644 --- a/src/client/telemetry/pylance.ts +++ b/src/client/telemetry/pylance.ts @@ -47,8 +47,6 @@ */ /* __GDPR__ "language_server/analysis_complete" : { - "common.remotename" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "common.uikind" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "configparseerroroccurred" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "elapsedms" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "externalmb" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -152,8 +150,6 @@ */ /* __GDPR__ "language_server/exception_intellicode" : { - "common.remotename" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "common.uikind" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "lsversion" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "failed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } @@ -199,8 +195,6 @@ "absoluteuserunresolved" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "builtinimportstubs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "builtinimporttotal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "common.remotename" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "common.uikind" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "installsource" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "localimportstubs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "localimporttotal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -255,8 +249,6 @@ /* __GDPR__ "language_server/intellicode_completion_item_selected" : { "class" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "common.remotename" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "common.uikind" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "elapsedtime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "failurereason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, @@ -277,8 +269,6 @@ */ /* __GDPR__ "language_server/intellicode_enabled" : { - "common.remotename" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "common.uikind" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "enabled" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "installsource" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "lsversion" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, diff --git a/src/client/telemetry/types.ts b/src/client/telemetry/types.ts index 865dca278bf0..42e51b261129 100644 --- a/src/client/telemetry/types.ts +++ b/src/client/telemetry/types.ts @@ -9,7 +9,6 @@ import { EventName } from './constants'; export type EditorLoadTelemetry = IEventNamePropertyMapping[EventName.EDITOR_LOAD]; export type PythonInterpreterTelemetry = IEventNamePropertyMapping[EventName.PYTHON_INTERPRETER]; -export type DebuggerTelemetry = IEventNamePropertyMapping[EventName.DEBUGGER]; export type TestTool = 'pytest' | 'unittest'; export type TestRunTelemetry = IEventNamePropertyMapping[EventName.UNITTEST_RUN]; export type TestDiscoveryTelemetry = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_DONE]; diff --git a/src/client/terminals/codeExecution/terminalReplWatcher.ts b/src/client/terminals/codeExecution/terminalReplWatcher.ts index bab70cb2f654..951961ab6901 100644 --- a/src/client/terminals/codeExecution/terminalReplWatcher.ts +++ b/src/client/terminals/codeExecution/terminalReplWatcher.ts @@ -3,16 +3,24 @@ import { onDidStartTerminalShellExecution } from '../../common/vscodeApis/window import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; -function checkREPLCommand(command: string): boolean { +function checkREPLCommand(command: string): undefined | 'manualTerminal' | `runningScript` { const lower = command.toLowerCase().trimStart(); - return lower.startsWith('python') || lower.startsWith('py '); + if (lower.startsWith('python') || lower.startsWith('py ')) { + const parts = lower.split(' '); + if (parts.length === 1) { + return 'manualTerminal'; + } + return 'runningScript'; + } + return undefined; } export function registerTriggerForTerminalREPL(disposables: Disposable[]): void { disposables.push( onDidStartTerminalShellExecution(async (e: TerminalShellExecutionStartEvent) => { - if (e.execution.commandLine.isTrusted && checkREPLCommand(e.execution.commandLine.value)) { - sendTelemetryEvent(EventName.REPL, undefined, { replType: 'manualTerminal' }); + const replType = checkREPLCommand(e.execution.commandLine.value); + if (e.execution.commandLine.isTrusted && replType) { + sendTelemetryEvent(EventName.REPL, undefined, { replType }); } }), ); diff --git a/src/client/terminals/envCollectionActivation/service.ts b/src/client/terminals/envCollectionActivation/service.ts index a527abe90454..43b8ceeb8e06 100644 --- a/src/client/terminals/envCollectionActivation/service.ts +++ b/src/client/terminals/envCollectionActivation/service.ts @@ -472,6 +472,7 @@ function shouldSkip(env: string) { 'PYTHONUTF8', // We have deactivate service which takes care of setting it. '_OLD_VIRTUAL_PATH', + 'PWD', ].includes(env); } diff --git a/src/client/terminals/pythonStartup.ts b/src/client/terminals/pythonStartup.ts index 542a2e6a6355..28878713a8db 100644 --- a/src/client/terminals/pythonStartup.ts +++ b/src/client/terminals/pythonStartup.ts @@ -3,10 +3,10 @@ import { ExtensionContext, Uri } from 'vscode'; import * as path from 'path'; -import { copy, createDirectory, getConfiguration } from '../common/vscodeApis/workspaceApis'; +import { copy, createDirectory, getConfiguration, onDidChangeConfiguration } from '../common/vscodeApis/workspaceApis'; import { EXTENSION_ROOT_DIR } from '../constants'; -export async function registerPythonStartup(context: ExtensionContext): Promise { +async function applyPythonStartupSetting(context: ExtensionContext): Promise { const config = getConfiguration('python'); const pythonrcSetting = config.get('terminal.shellIntegration.enabled'); @@ -25,3 +25,14 @@ export async function registerPythonStartup(context: ExtensionContext): Promise< context.environmentVariableCollection.delete('PYTHONSTARTUP'); } } + +export async function registerPythonStartup(context: ExtensionContext): Promise { + await applyPythonStartupSetting(context); + context.subscriptions.push( + onDidChangeConfiguration(async (e) => { + if (e.affectsConfiguration('python.terminal.shellIntegration.enabled')) { + await applyPythonStartupSetting(context); + } + }), + ); +} diff --git a/src/client/testing/configuration/types.ts b/src/client/testing/configuration/types.ts index 5da99398283b..3b759bcb39e8 100644 --- a/src/client/testing/configuration/types.ts +++ b/src/client/testing/configuration/types.ts @@ -11,6 +11,7 @@ export interface ITestingSettings { unittestArgs: string[]; cwd?: string; readonly autoTestDiscoverOnSaveEnabled: boolean; + readonly autoTestDiscoverOnSavePattern: string; } export type TestSettingsPropertyNames = { diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 68e10a2213d6..e3b37bf74e40 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -53,6 +53,8 @@ export async function writeTestIdsFile(testIds: string[]): Promise { try { traceLog('Attempting to use temp directory for test ids file, file name:', tempName); tempFileName = path.join(os.tmpdir(), tempName); + // attempt access to written file to check permissions + await fs.promises.access(os.tmpdir()); } catch (error) { // Handle the error when accessing the temp directory traceError('Error accessing temp directory:', error, ' Attempt to use extension root dir instead'); diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 6142140b3e2e..98a7f909a8e2 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -3,6 +3,7 @@ import { inject, injectable, named } from 'inversify'; import { uniq } from 'lodash'; +import * as minimatch from 'minimatch'; import { CancellationToken, TestController, @@ -552,7 +553,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc private watchForTestContentChangeOnSave(): void { this.disposables.push( onDidSaveTextDocument(async (doc: TextDocument) => { - if (doc.fileName.endsWith('.py')) { + const settings = this.configSettings.getSettings(doc.uri); + if (minimatch.default(doc.uri.fsPath, settings.testing.autoTestDiscoverOnSavePattern)) { traceVerbose(`Testing: Trigger refresh after saving ${doc.uri.fsPath}`); this.sendTriggerTelemetry('watching'); this.refreshData.trigger(doc.uri, false); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index ef68f7d8039d..71d71997c57e 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -111,7 +111,9 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const pythonPathCommand = [fullPluginPath, ...pythonPathParts].join(path.delimiter); mutableEnv.PYTHONPATH = pythonPathCommand; mutableEnv.TEST_RUN_PIPE = discoveryPipeName; - traceInfo(`All environment variables set for pytest discovery: ${JSON.stringify(mutableEnv)}`); + traceInfo( + `All environment variables set for pytest discovery, PYTHONPATH: ${JSON.stringify(mutableEnv.PYTHONPATH)}`, + ); // delete UUID following entire discovery finishing. const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); @@ -176,6 +178,9 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); + const execInfo = await execService?.getExecutablePath(); + traceVerbose(`Executable path for pytest discovery: ${execInfo}.`); + const deferredTillExecClose: Deferred = createTestingDeferred(); let resultProc: ChildProcess | undefined; diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index f66bff584fe2..3a824f79ac63 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -116,6 +116,10 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { }; // need to check what will happen in the exec service is NOT defined and is null const execService = await executionFactory?.createActivatedEnvironment(creationOptions); + + const execInfo = await execService?.getExecutablePath(); + traceVerbose(`Executable path for pytest execution: ${execInfo}.`); + try { // Remove positional test folders and files, we will add as needed per node let testArgs = removePositionalFoldersAndFiles(pytestArgs); @@ -133,7 +137,11 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { // create a file with the test ids and set the environment variable to the file name const testIdsFileName = await utils.writeTestIdsFile(testIds); mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; - traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); + traceInfo( + `All environment variables set for pytest execution, PYTHONPATH: ${JSON.stringify( + mutableEnv.PYTHONPATH, + )}`, + ); const spawnOptions: SpawnOptions = { cwd, diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 73eb3f5aec2b..7e478b25735a 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -28,7 +28,7 @@ import { fixLogLinesNoTrailing, startDiscoveryNamedPipe, } from '../common/utils'; -import { traceError, traceInfo, traceLog } from '../../../logging'; +import { traceError, traceInfo, traceLog, traceVerbose } from '../../../logging'; import { getEnvironment, runInBackground, useEnvExtension } from '../../../envExt/api.internal'; /** @@ -169,6 +169,8 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { resource: options.workspaceFolder, }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); + const execInfo = await execService?.getExecutablePath(); + traceVerbose(`Executable path for unittest discovery: ${execInfo}.`); let resultProc: ChildProcess | undefined; options.token?.onCancellationRequested(() => { diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index e2b591379335..e46e8c436583 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -14,7 +14,7 @@ import { TestCommandOptions, TestExecutionCommand, } from '../common/types'; -import { traceError, traceInfo, traceLog } from '../../../logging'; +import { traceError, traceInfo, traceLog, traceVerbose } from '../../../logging'; import { MESSAGE_ON_TESTING_OUTPUT_MOVE, fixLogLinesNoTrailing } from '../common/utils'; import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../../common/variables/types'; import { @@ -130,7 +130,11 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { // create named pipe server to send test ids const testIdsFileName = await utils.writeTestIdsFile(testIds); mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; - traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); + traceInfo( + `All environment variables set for unittest execution, PYTHONPATH: ${JSON.stringify( + mutableEnv.PYTHONPATH, + )}`, + ); const spawnOptions: SpawnOptions = { token: options.token, @@ -145,6 +149,10 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { resource: options.workspaceFolder, }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); + + const execInfo = await execService?.getExecutablePath(); + traceVerbose(`Executable path for unittest execution: ${execInfo}.`); + const args = [options.command.script].concat(options.command.args); if (options.outChannel) { diff --git a/src/test/api.functional.test.ts b/src/test/api.functional.test.ts index eea0fb920b15..1149dcb7da9d 100644 --- a/src/test/api.functional.test.ts +++ b/src/test/api.functional.test.ts @@ -19,6 +19,8 @@ import { ServiceManager } from '../client/ioc/serviceManager'; import { IServiceContainer, IServiceManager } from '../client/ioc/types'; import { IDiscoveryAPI } from '../client/pythonEnvironments/base/locator'; import * as pythonDebugger from '../client/debugger/pythonDebugger'; +import { JupyterExtensionPythonEnvironments, JupyterPythonEnvironmentApi } from '../client/jupyter/jupyterIntegration'; +import { EventEmitter, Uri } from 'vscode'; suite('Extension API', () => { const debuggerPath = path.join(EXTENSION_ROOT_DIR, 'python_files', 'lib', 'python', 'debugpy'); @@ -49,6 +51,14 @@ suite('Extension API', () => { instance(environmentVariablesProvider), ); when(serviceContainer.get(IInterpreterService)).thenReturn(instance(interpreterService)); + const onDidChangePythonEnvironment = new EventEmitter(); + const jupyterApi: JupyterPythonEnvironmentApi = { + onDidChangePythonEnvironment: onDidChangePythonEnvironment.event, + getPythonEnvironment: (_uri: Uri) => undefined, + }; + when(serviceContainer.get(JupyterExtensionPythonEnvironments)).thenReturn( + jupyterApi, + ); when(serviceContainer.get(IDisposableRegistry)).thenReturn([]); getDebugpyPathStub = sinon.stub(pythonDebugger, 'getDebugpyPath'); getDebugpyPathStub.resolves(debuggerPath); diff --git a/src/test/common/moduleInstaller.test.ts b/src/test/common/moduleInstaller.test.ts index 0b900b97c4c0..0cdb6f270c54 100644 --- a/src/test/common/moduleInstaller.test.ts +++ b/src/test/common/moduleInstaller.test.ts @@ -13,7 +13,6 @@ import { CommandManager } from '../../client/common/application/commandManager'; import { ReloadVSCodeCommandHandler } from '../../client/common/application/commands/reloadCommand'; import { ReportIssueCommandHandler } from '../../client/common/application/commands/reportIssueCommand'; import { DebugService } from '../../client/common/application/debugService'; -import { DebugSessionTelemetry } from '../../client/common/application/debugSessionTelemetry'; import { DocumentManager } from '../../client/common/application/documentManager'; import { Extensions } from '../../client/common/application/extensions'; import { @@ -259,10 +258,6 @@ suite('Module Installer', () => { IExtensionSingleActivationService, ReportIssueCommandHandler, ); - ioc.serviceManager.addSingleton( - IExtensionSingleActivationService, - DebugSessionTelemetry, - ); } test('Ensure pip is supported and conda is not', async () => { ioc.serviceManager.addSingletonInstance( diff --git a/src/test/common/persistentState.unit.test.ts b/src/test/common/persistentState.unit.test.ts index 9af28e2f5860..a77ee571559e 100644 --- a/src/test/common/persistentState.unit.test.ts +++ b/src/test/common/persistentState.unit.test.ts @@ -5,6 +5,7 @@ import { assert, expect } from 'chai'; import * as TypeMoq from 'typemoq'; +import * as sinon from 'sinon'; import { Memento } from 'vscode'; import { ICommandManager } from '../../client/common/application/types'; import { Commands } from '../../client/common/constants'; @@ -17,17 +18,25 @@ import { import { IDisposable } from '../../client/common/types'; import { sleep } from '../core'; import { MockMemento } from '../mocks/mementos'; +import * as apiInt from '../../client/envExt/api.internal'; suite('Persistent State', () => { let cmdManager: TypeMoq.IMock; let persistentStateFactory: PersistentStateFactory; let workspaceMemento: Memento; let globalMemento: Memento; + let useEnvExtensionStub: sinon.SinonStub; setup(() => { cmdManager = TypeMoq.Mock.ofType(); workspaceMemento = new MockMemento(); globalMemento = new MockMemento(); persistentStateFactory = new PersistentStateFactory(globalMemento, workspaceMemento, cmdManager.object); + + useEnvExtensionStub = sinon.stub(apiInt, 'useEnvExtension'); + useEnvExtensionStub.returns(false); + }); + teardown(() => { + sinon.restore(); }); test('Global states created are restored on invoking clean storage command', async () => { diff --git a/src/test/common/process/pythonEnvironment.unit.test.ts b/src/test/common/process/pythonEnvironment.unit.test.ts index f4b11bb97cd5..a2cca66d08be 100644 --- a/src/test/common/process/pythonEnvironment.unit.test.ts +++ b/src/test/common/process/pythonEnvironment.unit.test.ts @@ -284,7 +284,7 @@ suite('CondaEnvironment', () => { teardown(() => sinon.restore()); - test('getExecutionInfo with a named environment should return execution info using the environment name', async () => { + test('getExecutionInfo with a named environment should return execution info using the environment path', async () => { const condaInfo = { name: 'foo', path: 'bar' }; const env = await createCondaEnv(condaInfo, processService.object, fileSystem.object); @@ -292,8 +292,8 @@ suite('CondaEnvironment', () => { expect(result).to.deep.equal({ command: condaFile, - args: ['run', '-n', condaInfo.name, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT, ...args], - python: [condaFile, 'run', '-n', condaInfo.name, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], + args: ['run', '-p', condaInfo.path, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT, ...args], + python: [condaFile, 'run', '-p', condaInfo.path, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], pythonExecutable: pythonPath, }); }); @@ -312,12 +312,12 @@ suite('CondaEnvironment', () => { }); }); - test('getExecutionObservableInfo with a named environment should return execution info using conda full path with the name', async () => { + test('getExecutionObservableInfo with a named environment should return execution info using conda full path with the path', async () => { const condaInfo = { name: 'foo', path: 'bar' }; const expected = { command: condaFile, - args: ['run', '-n', condaInfo.name, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT, ...args], - python: [condaFile, 'run', '-n', condaInfo.name, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], + args: ['run', '-p', condaInfo.path, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT, ...args], + python: [condaFile, 'run', '-p', condaInfo.path, '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], pythonExecutable: pythonPath, }; const env = await createCondaEnv(condaInfo, processService.object, fileSystem.object); diff --git a/src/test/common/terminals/service.unit.test.ts b/src/test/common/terminals/service.unit.test.ts index 9903f6781f28..63a1cd544940 100644 --- a/src/test/common/terminals/service.unit.test.ts +++ b/src/test/common/terminals/service.unit.test.ts @@ -11,6 +11,7 @@ import { TerminalShellExecution, TerminalShellExecutionEndEvent, TerminalShellIntegration, + Uri, Terminal as VSCodeTerminal, WorkspaceConfiguration, } from 'vscode'; @@ -18,7 +19,12 @@ import { ITerminalManager, IWorkspaceService } from '../../../client/common/appl import { EXTENSION_ROOT_DIR } from '../../../client/common/constants'; import { IPlatformService } from '../../../client/common/platform/types'; import { TerminalService } from '../../../client/common/terminal/service'; -import { ITerminalActivator, ITerminalHelper, TerminalShellType } from '../../../client/common/terminal/types'; +import { + ITerminalActivator, + ITerminalHelper, + TerminalCreationOptions, + TerminalShellType, +} from '../../../client/common/terminal/types'; import { IDisposableRegistry } from '../../../client/common/types'; import { IServiceContainer } from '../../../client/ioc/types'; import { ITerminalAutoActivation } from '../../../client/terminals/types'; @@ -26,6 +32,8 @@ import { createPythonInterpreter } from '../../utils/interpreters'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; import * as platform from '../../../client/common/utils/platform'; import * as extapi from '../../../client/envExt/api.internal'; +import { IInterpreterService } from '../../../client/interpreter/contracts'; +import { PythonEnvironment } from '../../../client/pythonEnvironments/info'; suite('Terminal Service', () => { let service: TerminalService; @@ -46,6 +54,8 @@ suite('Terminal Service', () => { let editorConfig: TypeMoq.IMock; let isWindowsStub: sinon.SinonStub; let useEnvExtensionStub: sinon.SinonStub; + let interpreterService: TypeMoq.IMock; + let options: TypeMoq.IMock; setup(() => { useEnvExtensionStub = sinon.stub(extapi, 'useEnvExtension'); @@ -92,6 +102,13 @@ suite('Terminal Service', () => { disposables = []; mockServiceContainer = TypeMoq.Mock.ofType(); + interpreterService = TypeMoq.Mock.ofType(); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); + + options = TypeMoq.Mock.ofType(); + options.setup((o) => o.resource).returns(() => Uri.parse('a')); mockServiceContainer.setup((c) => c.get(ITerminalManager)).returns(() => terminalManager.object); mockServiceContainer.setup((c) => c.get(ITerminalHelper)).returns(() => terminalHelper.object); @@ -100,6 +117,7 @@ suite('Terminal Service', () => { mockServiceContainer.setup((c) => c.get(IWorkspaceService)).returns(() => workspaceService.object); mockServiceContainer.setup((c) => c.get(ITerminalActivator)).returns(() => terminalActivator.object); mockServiceContainer.setup((c) => c.get(ITerminalAutoActivation)).returns(() => terminalAutoActivator.object); + mockServiceContainer.setup((c) => c.get(IInterpreterService)).returns(() => interpreterService.object); getConfigurationStub = sinon.stub(workspaceApis, 'getConfiguration'); isWindowsStub = sinon.stub(platform, 'isWindows'); pythonConfig = TypeMoq.Mock.ofType(); @@ -117,6 +135,7 @@ suite('Terminal Service', () => { } disposables.filter((item) => !!item).forEach((item) => item.dispose()); sinon.restore(); + interpreterService.reset(); }); test('Ensure terminal is disposed', async () => { @@ -239,7 +258,7 @@ suite('Terminal Service', () => { terminal.verify((t) => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.exactly(1)); }); - test('Ensure sendText is NOT called when Python shell integration and terminal shell integration are both enabled - Mac, Linux', async () => { + test('Ensure sendText is NOT called when Python shell integration and terminal shell integration are both enabled - Mac, Linux && Python < 3.13', async () => { isWindowsStub.returns(false); pythonConfig .setup((p) => p.get('terminal.shellIntegration.enabled')) @@ -261,6 +280,36 @@ suite('Terminal Service', () => { terminal.verify((t) => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.never()); }); + test('Ensure sendText is called when Python shell integration and terminal shell integration are both enabled - Mac, Linux && Python >= 3.13', async () => { + interpreterService.reset(); + + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => + Promise.resolve({ path: 'yo', version: { major: 3, minor: 13, patch: 0 } } as PythonEnvironment), + ); + + isWindowsStub.returns(false); + pythonConfig + .setup((p) => p.get('terminal.shellIntegration.enabled')) + .returns(() => true) + .verifiable(TypeMoq.Times.once()); + + terminalHelper + .setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve(undefined)); + + service = new TerminalService(mockServiceContainer.object, options.object); + const textToSend = 'Some Text'; + terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash); + terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object); + + await service.ensureTerminal(); + await service.executeCommand(textToSend, true); + + terminal.verify((t) => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.once()); + }); + test('Ensure sendText IS called even when Python shell integration and terminal shell integration are both enabled - Window', async () => { isWindowsStub.returns(true); pythonConfig diff --git a/src/test/debugger/extension/adapter/factory.unit.test.ts b/src/test/debugger/extension/adapter/factory.unit.test.ts index fde87d930078..50984327e40d 100644 --- a/src/test/debugger/extension/adapter/factory.unit.test.ts +++ b/src/test/debugger/extension/adapter/factory.unit.test.ts @@ -23,7 +23,6 @@ import { IInterpreterService } from '../../../../client/interpreter/contracts'; import { InterpreterService } from '../../../../client/interpreter/interpreterService'; import { EnvironmentType } from '../../../../client/pythonEnvironments/info'; import { clearTelemetryReporter } from '../../../../client/telemetry'; -import { EventName } from '../../../../client/telemetry/constants'; import * as windowApis from '../../../../client/common/vscodeApis/windowApis'; import { PersistentState, PersistentStateFactory } from '../../../../client/common/persistentState'; import { ICommandManager } from '../../../../client/common/application/types'; @@ -269,16 +268,12 @@ suite('Debugging - Adapter Factory', () => { test('Send attach to local process telemetry if attaching to a local process', async () => { const session = createSession({ request: 'attach', processId: 1234 }); await factory.createDebugAdapterDescriptor(session, nodeExecutable); - - assert.ok(Reporter.eventNames.includes(EventName.DEBUGGER_ATTACH_TO_LOCAL_PROCESS)); }); test("Don't send any telemetry if not attaching to a local process", async () => { const session = createSession({}); await factory.createDebugAdapterDescriptor(session, nodeExecutable); - - assert.ok(Reporter.eventNames.includes(EventName.DEBUG_ADAPTER_USING_WHEELS_PATH)); }); test('Use "debugAdapterPath" when specified', async () => { diff --git a/src/test/environmentApi.unit.test.ts b/src/test/environmentApi.unit.test.ts index 012e1a0bfc69..2e5d13161f7b 100644 --- a/src/test/environmentApi.unit.test.ts +++ b/src/test/environmentApi.unit.test.ts @@ -38,6 +38,7 @@ import { EnvironmentsChangeEvent, PythonExtension, } from '../client/api/types'; +import { JupyterPythonEnvironmentApi } from '../client/jupyter/jupyterIntegration'; suite('Python Environment API', () => { const workspacePath = 'path/to/workspace'; @@ -80,7 +81,6 @@ suite('Python Environment API', () => { onDidChangeRefreshState = new EventEmitter(); onDidChangeEnvironments = new EventEmitter(); onDidChangeEnvironmentVariables = new EventEmitter(); - serviceContainer.setup((s) => s.get(IExtensions)).returns(() => extensions.object); serviceContainer.setup((s) => s.get(IInterpreterPathService)).returns(() => interpreterPathService.object); serviceContainer.setup((s) => s.get(IConfigurationService)).returns(() => configService.object); @@ -94,8 +94,13 @@ 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(() => []); + const onDidChangePythonEnvironment = new EventEmitter(); + const jupyterApi: JupyterPythonEnvironmentApi = { + onDidChangePythonEnvironment: onDidChangePythonEnvironment.event, + getPythonEnvironment: (_uri: Uri) => undefined, + }; - environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object); + environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object, jupyterApi); }); teardown(() => { @@ -323,7 +328,12 @@ suite('Python Environment API', () => { }, ]; discoverAPI.setup((d) => d.getEnvs()).returns(() => envs); - environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object); + const onDidChangePythonEnvironment = new EventEmitter(); + const jupyterApi: JupyterPythonEnvironmentApi = { + onDidChangePythonEnvironment: onDidChangePythonEnvironment.event, + getPythonEnvironment: (_uri: Uri) => undefined, + }; + environmentApi = buildEnvironmentApi(discoverAPI.object, serviceContainer.object, jupyterApi); const actual = environmentApi.known; const actualEnvs = actual?.map((a) => (a as EnvironmentReference).internal); assert.deepEqual( diff --git a/src/test/pythonEnvironments/common/environmentManagers/conda.unit.test.ts b/src/test/pythonEnvironments/common/environmentManagers/conda.unit.test.ts index e621c25aeb62..9480dffe6a59 100644 --- a/src/test/pythonEnvironments/common/environmentManagers/conda.unit.test.ts +++ b/src/test/pythonEnvironments/common/environmentManagers/conda.unit.test.ts @@ -536,7 +536,7 @@ suite('Conda and its environments are located correctly', () => { expect(args).to.not.equal(undefined); assert.deepStrictEqual( args, - ['conda', 'run', '-n', 'envName', '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], + ['conda', 'run', '-p', 'envPrefix', '--no-capture-output', 'python', OUTPUT_MARKER_SCRIPT], 'Incorrect args for case 1', ); diff --git a/src/test/terminals/shellIntegration/pythonStartup.test.ts b/src/test/terminals/shellIntegration/pythonStartup.test.ts index 06364c9445aa..45535d0ceecc 100644 --- a/src/test/terminals/shellIntegration/pythonStartup.test.ts +++ b/src/test/terminals/shellIntegration/pythonStartup.test.ts @@ -12,6 +12,7 @@ import { TerminalLinkContext, Terminal, EventEmitter, + workspace, } from 'vscode'; import { assert } from 'chai'; import * as workspaceApis from '../../../client/common/vscodeApis/workspaceApis'; @@ -35,6 +36,7 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { globalEnvironmentVariableCollection = TypeMoq.Mock.ofType(); context.setup((c) => c.environmentVariableCollection).returns(() => globalEnvironmentVariableCollection.object); context.setup((c) => c.storageUri).returns(() => Uri.parse('a')); + context.setup((c) => c.subscriptions).returns(() => []); globalEnvironmentVariableCollection .setup((c) => c.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) @@ -146,6 +148,17 @@ suite('Terminal - Shell Integration with PYTHONSTARTUP', () => { registerTerminalLinkProviderStub.restore(); }); + + test('Verify onDidChangeConfiguration is called when configuration changes', async () => { + const onDidChangeConfigurationSpy = sinon.spy(workspace, 'onDidChangeConfiguration'); + pythonConfig.setup((p) => p.get('terminal.shellIntegration.enabled')).returns(() => true); + + await registerPythonStartup(context.object); + + assert.isTrue(onDidChangeConfigurationSpy.calledOnce); + onDidChangeConfigurationSpy.restore(); + }); + if (process.platform === 'darwin') { test('Mac - Verify provideTerminalLinks returns links when context.line contains expectedNativeLink', () => { const provider = new CustomTerminalLinkProvider(); diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts index 852942715270..157134cdf276 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts @@ -71,6 +71,7 @@ suite('pytest test discovery adapter', () => { mockProc = new MockChildProcess('', ['']); execService = typeMoq.Mock.ofType(); execService.setup((p) => ((p as unknown) as any).then).returns(() => undefined); + execService.setup((x) => x.getExecutablePath()).returns(() => Promise.resolve('/mock/path/to/python')); outputChannel = typeMoq.Mock.ofType(); const output = new Observable>(() => { diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index 18cabcc96772..413c0af9406d 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -89,6 +89,8 @@ suite('pytest test execution adapter', () => { utilsStartRunResultNamedPipeStub = sinon.stub(util, 'startRunResultNamedPipe'); utilsStartRunResultNamedPipeStub.callsFake(() => Promise.resolve('runResultPipe-mockName')); + + execService.setup((x) => x.getExecutablePath()).returns(() => Promise.resolve('/mock/path/to/python')); }); teardown(() => { sinon.restore(); diff --git a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts index 911a5f89afb4..0a2cfad866d5 100644 --- a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts @@ -68,6 +68,7 @@ suite('Unittest test discovery adapter', () => { }, }; }); + execService.setup((x) => x.getExecutablePath()).returns(() => Promise.resolve('/mock/path/to/python')); execFactory = typeMoq.Mock.ofType(); deferred = createDeferred(); execFactory diff --git a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts index 78dcb0229e45..688d6d398101 100644 --- a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts @@ -88,6 +88,8 @@ suite('Unittest test execution adapter', () => { utilsStartRunResultNamedPipeStub = sinon.stub(util, 'startRunResultNamedPipe'); utilsStartRunResultNamedPipeStub.callsFake(() => Promise.resolve('runResultPipe-mockName')); + + execService.setup((x) => x.getExecutablePath()).returns(() => Promise.resolve('/mock/path/to/python')); }); teardown(() => { sinon.restore(); diff --git a/src/test/testing/testController/utils.unit.test.ts b/src/test/testing/testController/utils.unit.test.ts index b871d18348e2..ff1a0c707678 100644 --- a/src/test/testing/testController/utils.unit.test.ts +++ b/src/test/testing/testController/utils.unit.test.ts @@ -1,202 +1,50 @@ -// // Copyright (c) Microsoft Corporation. All rights reserved. -// // Licensed under the MIT License. +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { writeTestIdsFile } from '../../../client/testing/testController/common/utils'; +import { EXTENSION_ROOT_DIR } from '../../../client/constants'; -// import * as assert from 'assert'; -// import { -// JSONRPC_CONTENT_LENGTH_HEADER, -// JSONRPC_CONTENT_TYPE_HEADER, -// JSONRPC_UUID_HEADER, -// ExtractJsonRPCData, -// parseJsonRPCHeadersAndData, -// splitTestNameWithRegex, -// argKeyExists, -// addValueIfKeyNotExist, -// } from '../../../client/testing/testController/common/utils'; +suite('writeTestIdsFile tests', () => { + let sandbox: sinon.SinonSandbox; -// suite('Test Controller Utils: JSON RPC', () => { -// test('Empty raw data string', async () => { -// const rawDataString = ''; + setup(() => { + sandbox = sinon.createSandbox(); + }); -// const output = parseJsonRPCHeadersAndData(rawDataString); -// assert.deepStrictEqual(output.headers.size, 0); -// assert.deepStrictEqual(output.remainingRawData, ''); -// }); + teardown(() => { + sandbox.restore(); + }); -// test('Valid data empty JSON', async () => { -// const rawDataString = `${JSONRPC_CONTENT_LENGTH_HEADER}: 2\n${JSONRPC_CONTENT_TYPE_HEADER}: application/json\n${JSONRPC_UUID_HEADER}: 1234\n\n{}`; + test('should write test IDs to a temporary file', async () => { + const testIds = ['test1', 'test2', 'test3']; + const writeFileStub = sandbox.stub(fs.promises, 'writeFile').resolves(); -// const rpcHeaders = parseJsonRPCHeadersAndData(rawDataString); -// assert.deepStrictEqual(rpcHeaders.headers.size, 3); -// assert.deepStrictEqual(rpcHeaders.remainingRawData, '{}'); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, '{}'); -// }); + const result = await writeTestIdsFile(testIds); -// test('Valid data NO JSON', async () => { -// const rawDataString = `${JSONRPC_CONTENT_LENGTH_HEADER}: 0\n${JSONRPC_CONTENT_TYPE_HEADER}: application/json\n${JSONRPC_UUID_HEADER}: 1234\n\n`; + const tmpDir = os.tmpdir(); -// const rpcHeaders = parseJsonRPCHeadersAndData(rawDataString); -// assert.deepStrictEqual(rpcHeaders.headers.size, 3); -// assert.deepStrictEqual(rpcHeaders.remainingRawData, ''); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, ''); -// }); + assert.ok(result.startsWith(tmpDir)); -// test('Valid data with full JSON', async () => { -// // this is just some random JSON -// const json = -// '{"jsonrpc": "2.0", "method": "initialize", "params": {"processId": 1234, "rootPath": "/home/user/project", "rootUri": "file:///home/user/project", "capabilities": {}}, "id": 0}'; -// const rawDataString = `${JSONRPC_CONTENT_LENGTH_HEADER}: ${json.length}\n${JSONRPC_CONTENT_TYPE_HEADER}: application/json\n${JSONRPC_UUID_HEADER}: 1234\n\n${json}`; + assert.ok(writeFileStub.calledOnceWith(sinon.match.string, testIds.join('\n'))); + }); -// const rpcHeaders = parseJsonRPCHeadersAndData(rawDataString); -// assert.deepStrictEqual(rpcHeaders.headers.size, 3); -// assert.deepStrictEqual(rpcHeaders.remainingRawData, json); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, json); -// }); + test('should handle error when accessing temp directory', async () => { + const testIds = ['test1', 'test2', 'test3']; + const error = new Error('Access error'); + const accessStub = sandbox.stub(fs.promises, 'access').rejects(error); + const writeFileStub = sandbox.stub(fs.promises, 'writeFile').resolves(); + const mkdirStub = sandbox.stub(fs.promises, 'mkdir').resolves(); -// test('Valid data with multiple JSON', async () => { -// const json = -// '{"jsonrpc": "2.0", "method": "initialize", "params": {"processId": 1234, "rootPath": "/home/user/project", "rootUri": "file:///home/user/project", "capabilities": {}}, "id": 0}'; -// const rawDataString = `${JSONRPC_CONTENT_LENGTH_HEADER}: ${json.length}\n${JSONRPC_CONTENT_TYPE_HEADER}: application/json\n${JSONRPC_UUID_HEADER}: 1234\n\n${json}`; -// const rawDataString2 = rawDataString + rawDataString; + const result = await writeTestIdsFile(testIds); -// const rpcHeaders = parseJsonRPCHeadersAndData(rawDataString2); -// assert.deepStrictEqual(rpcHeaders.headers.size, 3); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, json); -// assert.deepStrictEqual(rpcContent.remainingRawData, rawDataString); -// }); + const tempFileFolder = path.join(EXTENSION_ROOT_DIR, '.temp'); -// test('Valid constant', async () => { -// const data = `{"cwd": "/Users/eleanorboyd/testingFiles/inc_dec_example", "status": "success", "result": {"test_dup_class.test_a.TestSomething.test_a": {"test": "test_dup_class.test_a.TestSomething.test_a", "outcome": "success", "message": "None", "traceback": null, "subtest": null}}}`; -// const secondPayload = `Content-Length: 270 -// Content-Type: application/json -// Request-uuid: 496c86b1-608f-4886-9436-ec00538e144c + assert.ok(result.startsWith(tempFileFolder)); -// ${data}`; -// const payload = `Content-Length: 270 -// Content-Type: application/json -// Request-uuid: 496c86b1-608f-4886-9436-ec00538e144c - -// ${data}${secondPayload}`; - -// const rpcHeaders = parseJsonRPCHeadersAndData(payload); -// assert.deepStrictEqual(rpcHeaders.headers.size, 3); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, data); -// assert.deepStrictEqual(rpcContent.remainingRawData, secondPayload); -// }); -// test('Valid content length as only header with carriage return', async () => { -// const payload = `Content-Length: 7 -// `; - -// const rpcHeaders = parseJsonRPCHeadersAndData(payload); -// assert.deepStrictEqual(rpcHeaders.headers.size, 1); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, ''); -// assert.deepStrictEqual(rpcContent.remainingRawData, ''); -// }); -// test('Valid content length header with no value', async () => { -// const payload = `Content-Length:`; - -// const rpcHeaders = parseJsonRPCHeadersAndData(payload); -// const rpcContent = ExtractJsonRPCData(rpcHeaders.headers.get('Content-Length'), rpcHeaders.remainingRawData); -// assert.deepStrictEqual(rpcContent.extractedJSON, ''); -// assert.deepStrictEqual(rpcContent.remainingRawData, ''); -// }); - -// suite('Test Controller Utils: Other', () => { -// interface TestCase { -// name: string; -// input: string; -// expectedParent: string; -// expectedSubtest: string; -// } - -// const testCases: Array = [ -// { -// name: 'Single parameter, named', -// input: 'test_package.ClassName.test_method (param=value)', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '(param=value)', -// }, -// { -// name: 'Single parameter, unnamed', -// input: 'test_package.ClassName.test_method [value]', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '[value]', -// }, -// { -// name: 'Multiple parameters, named', -// input: 'test_package.ClassName.test_method (param1=value1, param2=value2)', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '(param1=value1, param2=value2)', -// }, -// { -// name: 'Multiple parameters, unnamed', -// input: 'test_package.ClassName.test_method [value1, value2]', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '[value1, value2]', -// }, -// { -// name: 'Names with special characters', -// input: 'test_package.ClassName.test_method (param1=value/1, param2=value+2)', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '(param1=value/1, param2=value+2)', -// }, -// { -// name: 'Names with spaces', -// input: 'test_package.ClassName.test_method ["a b c d"]', -// expectedParent: 'test_package.ClassName.test_method', -// expectedSubtest: '["a b c d"]', -// }, -// ]; - -// testCases.forEach((testCase) => { -// test(`splitTestNameWithRegex: ${testCase.name}`, () => { -// const splitResult = splitTestNameWithRegex(testCase.input); -// assert.deepStrictEqual(splitResult, [testCase.expectedParent, testCase.expectedSubtest]); -// }); -// }); -// }); -// suite('Test Controller Utils: Args Mapping', () => { -// suite('addValueIfKeyNotExist', () => { -// test('should add key-value pair if key does not exist', () => { -// const args = ['key1=value1', 'key2=value2']; -// const result = addValueIfKeyNotExist(args, 'key3', 'value3'); -// assert.deepEqual(result, ['key1=value1', 'key2=value2', 'key3=value3']); -// }); - -// test('should not add key-value pair if key already exists', () => { -// const args = ['key1=value1', 'key2=value2']; -// const result = addValueIfKeyNotExist(args, 'key1', 'value3'); -// assert.deepEqual(result, ['key1=value1', 'key2=value2']); -// }); -// test('should not add key-value pair if key exists as a solo element', () => { -// const args = ['key1=value1', 'key2']; -// const result = addValueIfKeyNotExist(args, 'key2', 'yellow'); -// assert.deepEqual(result, ['key1=value1', 'key2']); -// }); -// test('add just key if value is null', () => { -// const args = ['key1=value1', 'key2']; -// const result = addValueIfKeyNotExist(args, 'key3', null); -// assert.deepEqual(result, ['key1=value1', 'key2', 'key3']); -// }); -// }); - -// suite('argKeyExists', () => { -// test('should return true if key exists', () => { -// const args = ['key1=value1', 'key2=value2']; -// const result = argKeyExists(args, 'key1'); -// assert.deepEqual(result, true); -// }); - -// test('should return false if key does not exist', () => { -// const args = ['key1=value1', 'key2=value2']; -// const result = argKeyExists(args, 'key3'); -// assert.deepEqual(result, false); -// }); -// }); -// }); -// }); + assert.ok(accessStub.called); + assert.ok(mkdirStub.called); + assert.ok(writeFileStub.calledOnceWith(sinon.match.string, testIds.join('\n'))); + }); +});