diff --git a/README.md b/README.md index a24bcc4..05ab421 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Mypy extension for Visual Studio Code -A Visual Studio Code extension with support for the `mypy` linter. The extension ships with `mypy=1.5.1`. +A Visual Studio Code extension with support for the `mypy` linter. The extension ships with `mypy=1.6.1`. For more information on `mypy`, see https://www.mypy-lang.org/. @@ -20,13 +20,15 @@ If you want to disable mypy, you can [disable this extension](https://code.visua | Settings | Default | Description | | ----------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | mypy-type-checker.args | `[]` | Custom arguments passed to `mypy`. E.g `"mypy-type-checker.args" = ["--config-file="]` | +| mypy-type-checker.cwd | `${workspaceFolder}` | This setting specifies the working directory for `mypy`. By default, it uses the root directory of the workspace `${workspaceFolder}`. If you want `mypy` to operate within the directory of the file currently being linted, you can set this to `${fileDirname}`. | | mypy-type-checker.severity | `{ "error": "Error", "note": "Information" }` | Controls mapping of severity from `mypy` to VS Code severity when displaying in the problems window. You can override specific `mypy` error codes `{ "error": "Error", "note": "Information", "name-defined": "Warning" }` | | mypy-type-checker.path | `[]` | Setting to provide custom `mypy` executable. This will slow down linting, since we will have to run `mypy` executable every time or file save or open. Example 1: `["~/global_env/mypy"]` Example 2: `["conda", "run", "-n", "lint_env", "python", "-m", "mypy"]` | | mypy-type-checker.interpreter | `[]` | Path to a Python interpreter to use to run the linter server. When set to `[]`, the interpreter for the workspace is obtained from `ms-python.python` extension. If set to some path, that path takes precedence, and the Python extension is not queried for the interpreter. | | mypy-type-checker.importStrategy | `useBundled` | Setting to choose where to load `mypy` from. `useBundled` picks mypy bundled with the extension. `fromEnvironment` uses `mypy` available in the environment. | | mypy-type-checker.showNotifications | `off` | Setting to control when a notification is shown. | | mypy-type-checker.reportingScope | `file` | (experimental) Setting to control if problems are reported for files open in the editor (`file`) or for the entire workspace (`workspace`). | -| mypy-type-checker.preferDaemon | true | (experimental) Setting to control how to invoke mypy. If true, `dmypy` is preferred over mypy; otherwise, mypy is preferred. Be aware, that the latter may slow down linting since it requires the `mypy` executable to be run whenever a file is saved or opened. Note that this setting will be overridden if `mypy-type-checker.path` is set. | +| mypy-type-checker.preferDaemon | `true` | (experimental) Setting to control how to invoke mypy. If true, `dmypy` is preferred over mypy; otherwise, mypy is preferred. Be aware, that the latter may slow down linting since it requires the `mypy` executable to be run whenever a file is saved or opened. Note that this setting will be overridden if `mypy-type-checker.path` is set. | +| mypy-type-checker.ignorePatterns | `[]` | Glob patterns used to exclude files and directories from being linted. | ## Commands diff --git a/bundled/tool/lsp_server.py b/bundled/tool/lsp_server.py index 94265b3..4bfde5b 100644 --- a/bundled/tool/lsp_server.py +++ b/bundled/tool/lsp_server.py @@ -9,6 +9,7 @@ import pathlib import re import sys +import sysconfig import tempfile import traceback import uuid @@ -28,6 +29,26 @@ def update_sys_path(path_to_add: str, strategy: str) -> None: sys.path.append(path_to_add) +# ********************************************************** +# Update PATH before running anything. +# ********************************************************** +def update_environ_path() -> None: + """Update PATH environment variable with the 'scripts' directory. + Windows: .venv/Scripts + Linux/MacOS: .venv/bin + """ + scripts = sysconfig.get_path("scripts") + paths_variants = ["Path", "PATH"] + + for var_name in paths_variants: + if var_name in os.environ: + paths = os.environ[var_name].split(os.pathsep) + if scripts not in paths: + paths.insert(0, scripts) + os.environ[var_name] = os.pathsep.join(paths) + break + + # Ensure that we can import LSP libraries, and other bundled libraries. BUNDLE_DIR = pathlib.Path(__file__).parent.parent BUNDLED_LIBS = os.fspath(BUNDLE_DIR / "libs") @@ -37,6 +58,7 @@ def update_sys_path(path_to_add: str, strategy: str) -> None: BUNDLED_LIBS, os.getenv("LS_IMPORT_STRATEGY", "useBundled"), ) +update_environ_path() # ********************************************************** # Imports needed for the language server goes below this. @@ -117,7 +139,7 @@ def _run_unidentified_tool( This is supposed to be called only in `get_mypy_info`. """ - cwd = settings["cwd"] + cwd = get_cwd(settings, None) if settings["path"]: argv = settings["path"] @@ -158,7 +180,7 @@ def did_close(params: lsp.DidCloseTextDocumentParams) -> None: settings = _get_settings_by_document(document) if settings["reportingScope"] == "file": # Publishing empty diagnostics to clear the entries for this file. - LSP_SERVER.publish_diagnostics(document.uri, []) + _clear_diagnostics(document) def _is_empty_diagnostics( @@ -175,6 +197,10 @@ def _is_empty_diagnostics( _reported_file_paths = set() +def _clear_diagnostics(document: workspace.Document) -> None: + LSP_SERVER.publish_diagnostics(document.uri, []) + + def _linting_helper(document: workspace.Document) -> None: global _reported_file_paths try: @@ -183,6 +209,28 @@ def _linting_helper(document: workspace.Document) -> None: # deep copy here to prevent accidentally updating global settings. settings = copy.deepcopy(_get_settings_by_document(document)) + if str(document.uri).startswith("vscode-notebook-cell"): + # We don't support running mypy on notebook cells. + log_warning(f"Skipping notebook cells [Not Supported]: {str(document.uri)}") + _clear_diagnostics(document) + return None + + if settings["reportingScope"] == "file" and utils.is_stdlib_file(document.path): + log_warning( + f"Skipping standard library file (stdlib excluded): {document.path}" + ) + _clear_diagnostics(document) + return None + + if settings["reportingScope"] == "file" and utils.is_match( + settings["ignorePatterns"], document.path + ): + log_warning( + f"Skipping file due to `mypy-type-checker.ignorePatterns` match: {document.path}" + ) + _clear_diagnostics(document) + return None + version = get_mypy_info(settings).version if (version.major, version.minor) >= (0, 991) and sys.version_info >= (3, 8): extra_args += ["--show-error-end"] @@ -210,7 +258,7 @@ def _linting_helper(document: workspace.Document) -> None: if _is_empty_diagnostics(document.path, parse_results): # Ensure that if nothing is returned for this document, at least # an empty diagnostic is returned to clear any old errors out. - LSP_SERVER.publish_diagnostics(document.uri, []) + _clear_diagnostics(document) if reportingScope == "workspace": for file_path in _reported_file_paths: @@ -218,7 +266,7 @@ def _linting_helper(document: workspace.Document) -> None: uri = uris.from_fs_path(file_path) LSP_SERVER.publish_diagnostics(uri, []) else: - LSP_SERVER.publish_diagnostics(document.uri, []) + _clear_diagnostics(document) except Exception: LSP_SERVER.show_message_log( f"Linting failed with error:\r\n{traceback.format_exc()}", @@ -447,10 +495,12 @@ def _get_global_defaults(): "note": "Information", }, ), + "ignorePatterns": [], "importStrategy": GLOBAL_SETTINGS.get("importStrategy", "useBundled"), "showNotifications": GLOBAL_SETTINGS.get("showNotifications", "off"), "extraPaths": GLOBAL_SETTINGS.get("extraPaths", []), "reportingScope": GLOBAL_SETTINGS.get("reportingScope", "file"), + "preferDaemon": GLOBAL_SETTINGS.get("preferDaemon", True), } @@ -590,27 +640,35 @@ def _get_env_vars(settings: Dict[str, Any]) -> Dict[str, str]: return new_env +def get_cwd(settings: Dict[str, Any], document: Optional[workspace.Document]) -> str: + """Returns cwd for the given settings and document.""" + if settings["cwd"] == "${workspaceFolder}": + return settings["workspaceFS"] + + if settings["cwd"] == "${fileDirname}": + if document is not None: + return os.fspath(pathlib.Path(document.path).parent) + return settings["workspaceFS"] + + return settings["cwd"] + + def _run_tool_on_document( document: workspace.Document, - extra_args: Sequence[str] = [], + extra_args: Sequence[str] = None, ) -> utils.RunResult | None: """Runs tool on the given document. if use_stdin is true then contents of the document is passed to the tool via stdin. """ - if str(document.uri).startswith("vscode-notebook-cell"): - # We don't support running mypy on notebook cells. - log_to_output("Skipping mypy on notebook cells.") - return None - - if utils.is_stdlib_file(document.path): - log_to_output("Skipping mypy on stdlib file: " + document.path) - return None + if extra_args is None: + extra_args = [] # deep copy here to prevent accidentally updating global settings. settings = copy.deepcopy(_get_settings_by_document(document)) - cwd = settings["cwd"] + + cwd = get_cwd(settings, document) if settings["path"]: argv = settings["path"] @@ -646,7 +704,7 @@ def _run_dmypy_command( log_error(f"dmypy command called in non-daemon context: {command}") raise ValueError(f"dmypy command called in non-daemon context: {command}") - cwd = settings["cwd"] + cwd = get_cwd(settings, None) if settings["path"]: argv = settings["path"] diff --git a/bundled/tool/lsp_utils.py b/bundled/tool/lsp_utils.py index 284a2f7..b955c29 100644 --- a/bundled/tool/lsp_utils.py +++ b/bundled/tool/lsp_utils.py @@ -87,6 +87,13 @@ def is_stdlib_file(file_path: str) -> bool: return any(normalized_path.startswith(path) for path in _stdlib_paths) +def is_match(patterns: List[str], file_path: str) -> bool: + """Returns true if the file matches one of the glob patterns.""" + if not patterns: + return False + return any(pathlib.Path(file_path).match(pattern) for pattern in patterns) + + # pylint: disable-next=too-few-public-methods class RunResult: """Object to hold result from running tool.""" diff --git a/package-lock.json b/package-lock.json index 0b19aba..3a0e198 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,27 +14,27 @@ "vscode-languageclient": "^8.1.0" }, "devDependencies": { - "@types/chai": "^4.3.6", - "@types/fs-extra": "^11.0.2", + "@types/chai": "^4.3.9", + "@types/fs-extra": "^11.0.3", "@types/glob": "^8.1.0", - "@types/mocha": "^10.0.2", + "@types/mocha": "^10.0.3", "@types/node": "16.x", - "@types/sinon": "^10.0.18", + "@types/sinon": "^10.0.20", "@types/vscode": "^1.74.0", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "@vscode/test-electron": "^2.3.5", "@vscode/vsce": "^2.21.1", "chai": "^4.3.10", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "glob": "^10.3.10", "mocha": "^10.2.0", "prettier": "^3.0.3", - "sinon": "^16.0.0", - "ts-loader": "^9.4.4", + "sinon": "^16.1.1", + "ts-loader": "^9.5.0", "typemoq": "^2.1.0", "typescript": "^5.2.2", - "webpack": "^5.88.2", + "webpack": "^5.89.0", "webpack-cli": "^5.1.4" }, "engines": { @@ -107,9 +107,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -372,9 +372,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "node_modules/@types/eslint": { @@ -404,9 +404,9 @@ "dev": true }, "node_modules/@types/fs-extra": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz", - "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.3.tgz", + "integrity": "sha512-sF59BlXtUdzEAL1u0MSvuzWd7PdZvZEtnaVkzX5mjpdWTJ8brG0jUqve3jPCzSzvAKKMHTG8F8o/WMQLtleZdQ==", "dev": true, "dependencies": { "@types/jsonfile": "*", @@ -445,9 +445,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.2.tgz", - "integrity": "sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "node_modules/@types/node": { @@ -457,15 +457,15 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.18", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.18.tgz", - "integrity": "sha512-OpQC9ug8BcnNxue2WF5aTruMaDRFn6NyfaE4DmAKOlQMn54b7CnCvDFV3wj5fk/HbSSTYmOYs2bTb5ShANjyQg==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -484,16 +484,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -519,15 +519,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" }, "engines": { @@ -547,13 +547,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -564,13 +564,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -591,9 +591,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -604,13 +604,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -631,17 +631,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" }, "engines": { @@ -656,12 +656,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1994,15 +1994,15 @@ } }, "node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4468,10 +4468,11 @@ } }, "node_modules/sinon": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", - "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.1.tgz", + "integrity": "sha512-tu0DS1g4Rm2xHT9mWK5g5aTogTXWwGGz3fQK/L5fnECPkcAQ3YcbAbJ4XxOkpDDnV4EV5n+lee5neq5QyVxoSg==", "dev": true, + "hasInstallScript": true, "dependencies": { "@sinonjs/commons": "^3.0.0", "@sinonjs/fake-timers": "^10.3.0", @@ -4809,15 +4810,16 @@ } }, "node_modules/ts-loader": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", - "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", + "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -4827,6 +4829,15 @@ "webpack": "^5.0.0" } }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -5030,9 +5041,9 @@ } }, "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -5494,9 +5505,9 @@ } }, "@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true }, "@humanwhocodes/config-array": { @@ -5705,9 +5716,9 @@ "dev": true }, "@types/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "@types/eslint": { @@ -5737,9 +5748,9 @@ "dev": true }, "@types/fs-extra": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz", - "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.3.tgz", + "integrity": "sha512-sF59BlXtUdzEAL1u0MSvuzWd7PdZvZEtnaVkzX5mjpdWTJ8brG0jUqve3jPCzSzvAKKMHTG8F8o/WMQLtleZdQ==", "dev": true, "requires": { "@types/jsonfile": "*", @@ -5778,9 +5789,9 @@ "dev": true }, "@types/mocha": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.2.tgz", - "integrity": "sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "@types/node": { @@ -5790,15 +5801,15 @@ "dev": true }, "@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "@types/sinon": { - "version": "10.0.18", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.18.tgz", - "integrity": "sha512-OpQC9ug8BcnNxue2WF5aTruMaDRFn6NyfaE4DmAKOlQMn54b7CnCvDFV3wj5fk/HbSSTYmOYs2bTb5ShANjyQg==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" @@ -5817,16 +5828,16 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -5836,54 +5847,54 @@ } }, "@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "requires": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" } }, "@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "requires": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5892,27 +5903,27 @@ } }, "@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "requires": { - "@typescript-eslint/types": "6.7.4", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" } }, @@ -6919,15 +6930,15 @@ "dev": true }, "eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -8782,9 +8793,9 @@ } }, "sinon": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", - "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.1.tgz", + "integrity": "sha512-tu0DS1g4Rm2xHT9mWK5g5aTogTXWwGGz3fQK/L5fnECPkcAQ3YcbAbJ4XxOkpDDnV4EV5n+lee5neq5QyVxoSg==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.0", @@ -9035,15 +9046,24 @@ "requires": {} }, "ts-loader": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", - "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", + "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", "dev": true, "requires": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } } }, "tunnel": { @@ -9207,9 +9227,9 @@ } }, "webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/package.json b/package.json index a734300..9c5ef10 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,16 @@ "scope": "resource", "type": "array" }, + "mypy-type-checker.cwd": { + "default": "${workspaceFolder}", + "description": "%settings.cwd.description%", + "scope": "resource", + "type": "string", + "examples": [ + "${workspaceFolder}/src", + "${fileDirname}" + ] + }, "mypy-type-checker.path": { "default": [], "markdownDescription": "%settings.path.description%", @@ -83,6 +93,22 @@ }, "type": "array" }, + "mypy-type-checker.ignorePatterns": { + "default": [], + "description": "%settings.ignorePatterns.description%", + "items": { + "type": "string" + }, + "scope": "resource", + "type": "array", + "uniqueItems": true, + "examples": [ + [ + "**/site-packages/**/*.py", + ".vscode/*.py" + ] + ] + }, "mypy-type-checker.importStrategy": { "default": "useBundled", "markdownDescription": "%settings.importStrategy.description%", @@ -184,27 +210,27 @@ "vscode-languageclient": "^8.1.0" }, "devDependencies": { - "@types/chai": "^4.3.6", - "@types/fs-extra": "^11.0.2", + "@types/chai": "^4.3.9", + "@types/fs-extra": "^11.0.3", "@types/glob": "^8.1.0", - "@types/mocha": "^10.0.2", + "@types/mocha": "^10.0.3", "@types/node": "16.x", - "@types/sinon": "^10.0.18", + "@types/sinon": "^10.0.20", "@types/vscode": "^1.74.0", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "@vscode/test-electron": "^2.3.5", "@vscode/vsce": "^2.21.1", "chai": "^4.3.10", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "glob": "^10.3.10", "mocha": "^10.2.0", "prettier": "^3.0.3", - "sinon": "^16.0.0", - "ts-loader": "^9.4.4", + "sinon": "^16.1.1", + "ts-loader": "^9.5.0", "typemoq": "^2.1.0", "typescript": "^5.2.2", - "webpack": "^5.88.2", + "webpack": "^5.89.0", "webpack-cli": "^5.1.4" } } diff --git a/package.nls.json b/package.nls.json index 95a3869..ed08343 100644 --- a/package.nls.json +++ b/package.nls.json @@ -2,8 +2,10 @@ "extension.description": "Linting support for Python files using `mypy`.", "command.restartServer": "Restart Server", "settings.args.description": "Arguments passed in. Each argument is a separate string in the array.", + "settings.cwd.description": "The current working directory used to run `mypy`. To use the directory of the file currently being linted, you can use `${fileDirname}` .", "settings.severity.description": "Mapping from severity of `mypy` message type to severity shown in problem window.", "settings.path.description": "When set to a path to `mypy` binary, extension will use that for linting. NOTE: Using this option may slowdown linting.", + "settings.ignorePatterns.description": "Patterns used to exclude files or folders from being linted. Applies to `reportingScope==file` only.", "settings.importStrategy.description": "Defines where `mypy` is imported from. This setting may be ignored if `mypy-type-checker.path` is set.", "settings.importStrategy.useBundled.description": "Always use the bundled version of `mypy`.", "settings.importStrategy.fromEnvironment.description": "Use `mypy` from environment, fallback to bundled version only if `mypy` not available in the environment.", diff --git a/requirements.txt b/requirements.txt index 26c8ece..6cc9b24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,34 +26,34 @@ lsprotocol==2023.0.0b1 \ --hash=sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62 \ --hash=sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4 # via pygls -mypy==1.5.1 \ - --hash=sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315 \ - --hash=sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0 \ - --hash=sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373 \ - --hash=sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a \ - --hash=sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161 \ - --hash=sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275 \ - --hash=sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693 \ - --hash=sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb \ - --hash=sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65 \ - --hash=sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4 \ - --hash=sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb \ - --hash=sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243 \ - --hash=sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14 \ - --hash=sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4 \ - --hash=sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1 \ - --hash=sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a \ - --hash=sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160 \ - --hash=sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25 \ - --hash=sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12 \ - --hash=sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d \ - --hash=sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92 \ - --hash=sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770 \ - --hash=sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2 \ - --hash=sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70 \ - --hash=sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb \ - --hash=sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5 \ - --hash=sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f +mypy==1.6.1 \ + --hash=sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7 \ + --hash=sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e \ + --hash=sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c \ + --hash=sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169 \ + --hash=sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208 \ + --hash=sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0 \ + --hash=sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1 \ + --hash=sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1 \ + --hash=sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7 \ + --hash=sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45 \ + --hash=sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143 \ + --hash=sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5 \ + --hash=sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f \ + --hash=sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd \ + --hash=sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245 \ + --hash=sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f \ + --hash=sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332 \ + --hash=sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30 \ + --hash=sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183 \ + --hash=sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f \ + --hash=sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85 \ + --hash=sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46 \ + --hash=sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71 \ + --hash=sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660 \ + --hash=sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb \ + --hash=sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c \ + --hash=sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a # via -r ./requirements.in mypy-extensions==1.0.0 \ --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ @@ -63,9 +63,9 @@ packaging==23.2 \ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 # via -r ./requirements.in -pygls==1.1.0 \ - --hash=sha256:70acb6fe0df1c8a17b7ce08daa0afdb4aedc6913a6a6696003e1434fda80a06e \ - --hash=sha256:eb19b818039d3d705ec8adbcdf5809a93af925f30cd7a3f3b7573479079ba00e +pygls==1.1.1 \ + --hash=sha256:330704551a335b443bf1cdfb0507f121608591095898d451f0007eeb1510067c \ + --hash=sha256:b1b4ddd6f800a5573f61f0ec2cd3bc7a859d171f48142b46e1de35a1357c00fe # via -r ./requirements.in tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ diff --git a/src/common/constants.ts b/src/common/constants.ts index e33f333..402e017 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -9,3 +9,6 @@ export const EXTENSION_ROOT_DIR = export const BUNDLED_PYTHON_SCRIPTS_DIR = path.join(EXTENSION_ROOT_DIR, 'bundled'); export const SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, 'tool', `lsp_server.py`); export const DEBUG_SERVER_SCRIPT_PATH = path.join(BUNDLED_PYTHON_SCRIPTS_DIR, 'tool', `_debug_server.py`); +export const PYTHON_MAJOR = 3; +export const PYTHON_MINOR = 8; +export const PYTHON_VERSION = `${PYTHON_MAJOR}.${PYTHON_MINOR}`; diff --git a/src/common/python.ts b/src/common/python.ts index ccd8a05..3294f2a 100644 --- a/src/common/python.ts +++ b/src/common/python.ts @@ -5,6 +5,7 @@ import { commands, Disposable, Event, EventEmitter, Uri } from 'vscode'; import { traceError, traceLog } from './logging'; import { PythonExtension, ResolvedEnvironment } from '@vscode/python-extension'; +import { PYTHON_MAJOR, PYTHON_MINOR, PYTHON_VERSION } from './constants'; export interface IInterpreterDetails { path?: string[]; @@ -70,11 +71,11 @@ export async function runPythonExtensionCommand(command: string, ...rest: any[]) export function checkVersion(resolved: ResolvedEnvironment | undefined): boolean { const version = resolved?.version; - if (version?.major === 3 && version?.minor >= 8) { + if (version?.major === PYTHON_MAJOR && version?.minor >= PYTHON_MINOR) { return true; } traceError(`Python version ${version?.major}.${version?.minor} is not supported.`); traceError(`Selected python path: ${resolved?.executable.uri?.fsPath}`); - traceError('Supported versions are 3.8 and above.'); + traceError(`Supported versions are ${PYTHON_VERSION} and above.`); return false; } diff --git a/src/common/server.ts b/src/common/server.ts index 413b073..77e9cda 100644 --- a/src/common/server.ts +++ b/src/common/server.ts @@ -14,7 +14,7 @@ import { DEBUG_SERVER_SCRIPT_PATH, SERVER_SCRIPT_PATH } from './constants'; import { traceError, traceInfo, traceVerbose } from './logging'; import { getDebuggerPath } from './python'; import { getExtensionSettings, getGlobalSettings, ISettings } from './settings'; -import { getDocumentSelector, getLSClientTraceLevel } from './utilities'; +import { getLSClientTraceLevel, getDocumentSelector } from './utilities'; import { updateStatus } from './status'; export type IInitOptions = { settings: ISettings[]; globalSettings: ISettings }; @@ -96,6 +96,7 @@ export async function restartServer( settings: await getExtensionSettings(serverId, true), globalSettings: await getGlobalSettings(serverId, false), }); + traceInfo(`Server: Start requested.`); _disposables.push( newLSClient.onDidChangeState((e) => { @@ -120,5 +121,6 @@ export async function restartServer( updateStatus(l10n.t('Server failed to start.'), LanguageStatusSeverity.Error); traceError(`Server: Start failed: ${ex}`); } + return newLSClient; } diff --git a/src/common/settings.ts b/src/common/settings.ts index a0afb7b..55bb526 100644 --- a/src/common/settings.ts +++ b/src/common/settings.ts @@ -2,9 +2,9 @@ // Licensed under the MIT License. import { ConfigurationChangeEvent, ConfigurationScope, WorkspaceConfiguration, WorkspaceFolder } from 'vscode'; +import { traceLog, traceWarn } from './logging'; import { getInterpreterDetails } from './python'; import { getConfiguration, getWorkspaceFolders } from './vscodeapi'; -import { traceLog } from './logging'; const DEFAULT_SEVERITY: Record = { error: 'Error', @@ -17,6 +17,7 @@ export interface ISettings { args: string[]; severity: Record; path: string[]; + ignorePatterns: string[]; interpreter: string[]; importStrategy: string; showNotifications: string; @@ -29,7 +30,12 @@ export function getExtensionSettings(namespace: string, includeInterpreter?: boo return Promise.all(getWorkspaceFolders().map((w) => getWorkspaceSettings(namespace, w, includeInterpreter))); } -function resolveVariables(value: string[], workspace?: WorkspaceFolder): string[] { +function resolveVariables( + value: string[], + workspace?: WorkspaceFolder, + interpreter?: string[], + env?: NodeJS.ProcessEnv, +): string[] { const substitutions = new Map(); const home = process.env.HOME || process.env.USERPROFILE; if (home) { @@ -43,7 +49,25 @@ function resolveVariables(value: string[], workspace?: WorkspaceFolder): string[ substitutions.set('${workspaceFolder:' + w.name + '}', w.uri.fsPath); }); - return value.map((s) => { + env = env || process.env; + if (env) { + for (const [key, value] of Object.entries(env)) { + if (value) { + substitutions.set('${env:' + key + '}', value); + } + } + } + + const modifiedValue = []; + for (const v of value) { + if (interpreter && v === '${interpreter}') { + modifiedValue.push(...interpreter); + } else { + modifiedValue.push(v); + } + } + + return modifiedValue.map((s) => { for (const [key, value] of substitutions) { s = s.replace(key, value); } @@ -51,36 +75,12 @@ function resolveVariables(value: string[], workspace?: WorkspaceFolder): string[ }); } -function getArgs(namespace: string, workspace: WorkspaceFolder): string[] { - const config = getConfiguration(namespace, workspace.uri); - const args = config.get('args', []); - return args; -} - -function getPath(namespace: string, workspace: WorkspaceFolder): string[] { - const config = getConfiguration(namespace, workspace.uri); - const path = config.get('path', []); - - if (path.length > 0) { - return path; - } - - return []; -} - -function getCwd(namespace: string, workspace: WorkspaceFolder): string { - const legacyConfig = getConfiguration('python', workspace.uri); - const legacyCwd = legacyConfig.get('linting.cwd'); - - if (legacyCwd) { - traceLog('Using cwd from `python.linting.cwd`.'); - return resolveVariables([legacyCwd], workspace)[0]; - } - - return workspace.uri.fsPath; +function getCwd(config: WorkspaceConfiguration, workspace: WorkspaceFolder): string { + const cwd = config.get('cwd', workspace.uri.fsPath); + return resolveVariables([cwd], workspace)[0]; } -function getExtraPaths(namespace: string, workspace: WorkspaceFolder): string[] { +function getExtraPaths(_namespace: string, workspace: WorkspaceFolder): string[] { const legacyConfig = getConfiguration('python', workspace.uri); const legacyExtraPaths = legacyConfig.get('analysis.extraPaths', []); @@ -100,39 +100,24 @@ export async function getWorkspaceSettings( workspace: WorkspaceFolder, includeInterpreter?: boolean, ): Promise { - const config = getConfiguration(namespace, workspace.uri); + const config = getConfiguration(namespace, workspace); let interpreter: string[] = []; if (includeInterpreter) { interpreter = getInterpreterFromSetting(namespace, workspace) ?? []; if (interpreter.length === 0) { - traceLog(`No interpreter found from setting ${namespace}.interpreter`); - traceLog(`Getting interpreter from ms-python.python extension for workspace ${workspace.uri.fsPath}`); interpreter = (await getInterpreterDetails(workspace.uri)).path ?? []; - if (interpreter.length > 0) { - traceLog( - `Interpreter from ms-python.python extension for ${workspace.uri.fsPath}:`, - `${interpreter.join(' ')}`, - ); - } - } else { - traceLog(`Interpreter from setting ${namespace}.interpreter: ${interpreter.join(' ')}`); - } - - if (interpreter.length === 0) { - traceLog(`No interpreter found for ${workspace.uri.fsPath} in settings or from ms-python.python extension`); } } - const args = getArgs(namespace, workspace); - const mypyPath = getPath(namespace, workspace); const extraPaths = getExtraPaths(namespace, workspace); const workspaceSetting = { - cwd: getCwd(namespace, workspace), + cwd: getCwd(config, workspace), workspace: workspace.uri.toString(), - args: resolveVariables(args, workspace), + args: resolveVariables(config.get('args', []), workspace), severity: config.get>('severity', DEFAULT_SEVERITY), - path: resolveVariables(mypyPath, workspace), + path: resolveVariables(config.get('path', []), workspace, interpreter), + ignorePatterns: resolveVariables(config.get('ignorePatterns', []), workspace), interpreter: resolveVariables(interpreter, workspace), importStrategy: config.get('importStrategy', 'useBundled'), showNotifications: config.get('showNotifications', 'off'), @@ -160,11 +145,12 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?: } const setting = { - cwd: process.cwd(), + cwd: getGlobalValue(config, 'cwd', process.cwd()), workspace: process.cwd(), args: getGlobalValue(config, 'args', []), severity: getGlobalValue>(config, 'severity', DEFAULT_SEVERITY), path: getGlobalValue(config, 'path', []), + ignorePatterns: getGlobalValue(config, 'ignorePatterns', []), interpreter: interpreter ?? [], importStrategy: getGlobalValue(config, 'importStrategy', 'useBundled'), showNotifications: getGlobalValue(config, 'showNotifications', 'off'), @@ -178,13 +164,59 @@ export async function getGlobalSettings(namespace: string, includeInterpreter?: export function checkIfConfigurationChanged(e: ConfigurationChangeEvent, namespace: string): boolean { const settings = [ `${namespace}.args`, + `${namespace}.cwd`, `${namespace}.severity`, `${namespace}.path`, `${namespace}.interpreter`, `${namespace}.importStrategy`, `${namespace}.showNotifications`, `${namespace}.reportingScope`, + `${namespace}.preferDaemon`, + `${namespace}.ignorePatterns`, + 'python.analysis.extraPaths', ]; const changed = settings.map((s) => e.affectsConfiguration(s)); return changed.includes(true); } + +export function logLegacySettings(namespace: string): void { + getWorkspaceFolders().forEach((workspace) => { + try { + const legacyConfig = getConfiguration('python', workspace.uri); + + const legacyMypyEnabled = legacyConfig.get('linting.mypyEnabled', false); + if (legacyMypyEnabled) { + traceWarn(`"python.linting.mypyEnabled" is deprecated. You can remove that setting.`); + traceWarn( + 'The mypy extension is always enabled. However, you can disable it per workspace using the extensions view.', + ); + traceWarn('You can exclude files and folders using the `python.linting.ignorePatterns` setting.'); + traceWarn( + `"python.linting.mypyEnabled" value for workspace ${workspace.uri.fsPath}: ${legacyMypyEnabled}`, + ); + } + + const legacyCwd = legacyConfig.get('linting.cwd'); + if (legacyCwd) { + traceWarn(`"python.linting.cwd" is deprecated. Use "${namespace}.cwd" instead.`); + traceWarn(`"python.linting.cwd" value for workspace ${workspace.uri.fsPath}: ${legacyCwd}`); + } + + const legacyArgs = legacyConfig.get('linting.mypyArgs', []); + if (legacyArgs.length > 0) { + traceWarn(`"python.linting.mypyArgs" is deprecated. Use "${namespace}.args" instead.`); + traceWarn(`"python.linting.mypyArgs" value for workspace ${workspace.uri.fsPath}:`); + traceWarn(`\n${JSON.stringify(legacyArgs, null, 4)}`); + } + + const legacyPath = legacyConfig.get('linting.mypyPath', ''); + if (legacyPath.length > 0 && legacyPath !== 'mypy') { + traceWarn(`"python.linting.mypyPath" is deprecated. Use "${namespace}.path" instead.`); + traceWarn(`"python.linting.mypyPath" value for workspace ${workspace.uri.fsPath}:`); + traceWarn(`\n${JSON.stringify(legacyPath, null, 4)}`); + } + } catch (err) { + traceWarn(`Error while logging legacy settings: ${err}`); + } + }); +} diff --git a/src/common/status.ts b/src/common/status.ts index 0f94604..910f8fe 100644 --- a/src/common/status.ts +++ b/src/common/status.ts @@ -3,8 +3,8 @@ import { LanguageStatusItem, Disposable, l10n, LanguageStatusSeverity } from 'vscode'; import { createLanguageStatusItem } from './vscodeapi'; -import { getDocumentSelector } from './utilities'; import { Command } from 'vscode-languageclient'; +import { getDocumentSelector } from './utilities'; let _status: LanguageStatusItem | undefined; export function registerLanguageStatusItem(id: string, name: string, command: string): Disposable { diff --git a/src/common/utilities.ts b/src/common/utilities.ts index c2385f8..0ff78d0 100644 --- a/src/common/utilities.ts +++ b/src/common/utilities.ts @@ -68,6 +68,7 @@ export async function getProjectRoot(): Promise { } export function getDocumentSelector(): DocumentSelector { + // virtual workspaces are not supported yet return isVirtualWorkspace() ? [{ language: 'python' }] : [ diff --git a/src/extension.ts b/src/extension.ts index 3d29a99..84cf409 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,14 +3,20 @@ import * as vscode from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; -import { restartServer } from './common/server'; import { registerLogger, traceError, traceLog, traceVerbose } from './common/logging'; import { initializePython, onDidChangePythonInterpreter } from './common/python'; -import { checkIfConfigurationChanged, getInterpreterFromSetting, getWorkspaceSettings } from './common/settings'; +import { restartServer } from './common/server'; +import { + checkIfConfigurationChanged, + getInterpreterFromSetting, + getWorkspaceSettings, + logLegacySettings, +} from './common/settings'; import { loadServerDefaults } from './common/setup'; -import { getProjectRoot } from './common/utilities'; +import { getLSClientTraceLevel, getProjectRoot } from './common/utilities'; import { createOutputChannel, onDidChangeConfiguration, registerCommand } from './common/vscodeapi'; import { registerLanguageStatusItem, updateStatus } from './common/status'; +import { PYTHON_VERSION } from './common/constants'; let lsClient: LanguageClient | undefined; export async function activate(context: vscode.ExtensionContext): Promise { @@ -24,6 +30,20 @@ export async function activate(context: vscode.ExtensionContext): Promise const outputChannel = createOutputChannel(serverName); context.subscriptions.push(outputChannel, registerLogger(outputChannel)); + const changeLogLevel = async (c: vscode.LogLevel, g: vscode.LogLevel) => { + const level = getLSClientTraceLevel(c, g); + await lsClient?.setTrace(level); + }; + + context.subscriptions.push( + outputChannel.onDidChangeLogLevel(async (e) => { + await changeLogLevel(e, vscode.env.logLevel); + }), + vscode.env.onDidChangeLogLevel(async (e) => { + await changeLogLevel(outputChannel.logLevel, e); + }), + ); + traceLog(`Name: ${serverName}`); traceLog(`Module: ${serverInfo.module}`); traceVerbose(`Configuration: ${JSON.stringify(serverInfo)}`); @@ -37,7 +57,7 @@ export async function activate(context: vscode.ExtensionContext): Promise 'Python interpreter missing:\r\n' + '[Option 1] Select python interpreter using the ms-python.python.\r\n' + `[Option 2] Set an interpreter using "${serverId}.interpreter" setting.\r\n`, - 'Please use Python 3.8 or greater.', + `Please use Python ${PYTHON_VERSION} or greater.`, ); } else { lsClient = await restartServer(workspaceSetting, serverId, serverName, outputChannel, lsClient); @@ -62,6 +82,11 @@ export async function activate(context: vscode.ExtensionContext): Promise registerLanguageStatusItem(serverId, serverName, `${serverId}.showLogs`), ); + // This is needed to inform users that they might have some legacy settings that + // are no longer supported. Instructions are printed in the output channel on how + // to update them. + logLegacySettings(serverId); + setImmediate(async () => { const interpreter = getInterpreterFromSetting(serverId); if (interpreter === undefined || interpreter.length === 0) { diff --git a/src/test/python_tests/lsp_test_client/defaults.py b/src/test/python_tests/lsp_test_client/defaults.py index 96fa062..547063e 100644 --- a/src/test/python_tests/lsp_test_client/defaults.py +++ b/src/test/python_tests/lsp_test_client/defaults.py @@ -5,215 +5,223 @@ """ import os +from typing import Any, Dict -from .constants import TEST_DATA +from .constants import PROJECT_ROOT from .utils import as_uri, get_initialization_options -VSCODE_DEFAULT_INITIALIZE = { - "processId": os.getpid(), - "clientInfo": {"name": "vscode", "version": "1.45.0"}, - "rootPath": str(TEST_DATA), - "rootUri": as_uri(str(TEST_DATA)), - "capabilities": { - "workspace": { - "applyEdit": True, - "workspaceEdit": { - "documentChanges": True, - "resourceOperations": ["create", "rename", "delete"], - "failureHandling": "textOnlyTransactional", - }, - "didChangeConfiguration": {"dynamicRegistration": True}, - "didChangeWatchedFiles": {"dynamicRegistration": True}, - "symbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - ] - }, - "tagSupport": {"valueSet": [1]}, - }, - "executeCommand": {"dynamicRegistration": True}, - "configuration": True, - "workspaceFolders": True, - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": True, - "versionSupport": False, - "tagSupport": {"valueSet": [1, 2]}, - "complexDiagnosticCodeSupport": True, - }, - "synchronization": { - "dynamicRegistration": True, - "willSave": True, - "willSaveWaitUntil": True, - "didSave": True, - }, - "completion": { - "dynamicRegistration": True, - "contextSupport": True, - "completionItem": { - "snippetSupport": True, - "commitCharactersSupport": True, - "documentationFormat": ["markdown", "plaintext"], - "deprecatedSupport": True, - "preselectSupport": True, - "tagSupport": {"valueSet": [1]}, - "insertReplaceSupport": True, - }, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - ] - }, - }, - "hover": { - "dynamicRegistration": True, - "contentFormat": ["markdown", "plaintext"], - }, - "signatureHelp": { - "dynamicRegistration": True, - "signatureInformation": { - "documentationFormat": ["markdown", "plaintext"], - "parameterInformation": {"labelOffsetSupport": True}, - }, - "contextSupport": True, - }, - "definition": {"dynamicRegistration": True, "linkSupport": True}, - "references": {"dynamicRegistration": True}, - "documentHighlight": {"dynamicRegistration": True}, - "documentSymbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - ] + +def vscode_initialize_defaults() -> Dict[str, Any]: + """Default initialize request params as generated by VS Code.""" + return dict( + { + "processId": os.getpid(), + "clientInfo": {"name": "vscode", "version": "1.45.0"}, + "rootPath": str(PROJECT_ROOT), + "rootUri": as_uri(str(PROJECT_ROOT)), + "capabilities": { + "workspace": { + "applyEdit": True, + "workspaceEdit": { + "documentChanges": True, + "resourceOperations": ["create", "rename", "delete"], + "failureHandling": "textOnlyTransactional", + }, + "didChangeConfiguration": {"dynamicRegistration": True}, + "didChangeWatchedFiles": {"dynamicRegistration": True}, + "symbol": { + "dynamicRegistration": True, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + ] + }, + "tagSupport": {"valueSet": [1]}, + }, + "executeCommand": {"dynamicRegistration": True}, + "configuration": True, + "workspaceFolders": True, }, - "hierarchicalDocumentSymbolSupport": True, - "tagSupport": {"valueSet": [1]}, - }, - "codeAction": { - "dynamicRegistration": True, - "isPreferredSupport": True, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports", - ] - } + "textDocument": { + "publishDiagnostics": { + "relatedInformation": True, + "versionSupport": False, + "tagSupport": {"valueSet": [1, 2]}, + "complexDiagnosticCodeSupport": True, + }, + "synchronization": { + "dynamicRegistration": True, + "willSave": True, + "willSaveWaitUntil": True, + "didSave": True, + }, + "completion": { + "dynamicRegistration": True, + "contextSupport": True, + "completionItem": { + "snippetSupport": True, + "commitCharactersSupport": True, + "documentationFormat": ["markdown", "plaintext"], + "deprecatedSupport": True, + "preselectSupport": True, + "tagSupport": {"valueSet": [1]}, + "insertReplaceSupport": True, + }, + "completionItemKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + ] + }, + }, + "hover": { + "dynamicRegistration": True, + "contentFormat": ["markdown", "plaintext"], + }, + "signatureHelp": { + "dynamicRegistration": True, + "signatureInformation": { + "documentationFormat": ["markdown", "plaintext"], + "parameterInformation": {"labelOffsetSupport": True}, + }, + "contextSupport": True, + }, + "definition": {"dynamicRegistration": True, "linkSupport": True}, + "references": {"dynamicRegistration": True}, + "documentHighlight": {"dynamicRegistration": True}, + "documentSymbol": { + "dynamicRegistration": True, + "symbolKind": { + "valueSet": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + ] + }, + "hierarchicalDocumentSymbolSupport": True, + "tagSupport": {"valueSet": [1]}, + }, + "codeAction": { + "dynamicRegistration": True, + "isPreferredSupport": True, + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite", + "source", + "source.organizeImports", + ] + } + }, + }, + "codeLens": {"dynamicRegistration": True}, + "formatting": {"dynamicRegistration": True}, + "rangeFormatting": {"dynamicRegistration": True}, + "onTypeFormatting": {"dynamicRegistration": True}, + "rename": {"dynamicRegistration": True, "prepareSupport": True}, + "documentLink": { + "dynamicRegistration": True, + "tooltipSupport": True, + }, + "typeDefinition": { + "dynamicRegistration": True, + "linkSupport": True, + }, + "implementation": { + "dynamicRegistration": True, + "linkSupport": True, + }, + "colorProvider": {"dynamicRegistration": True}, + "foldingRange": { + "dynamicRegistration": True, + "rangeLimit": 5000, + "lineFoldingOnly": True, + }, + "declaration": {"dynamicRegistration": True, "linkSupport": True}, + "selectionRange": {"dynamicRegistration": True}, }, + "window": {"workDoneProgress": True}, }, - "codeLens": {"dynamicRegistration": True}, - "formatting": {"dynamicRegistration": True}, - "rangeFormatting": {"dynamicRegistration": True}, - "onTypeFormatting": {"dynamicRegistration": True}, - "rename": {"dynamicRegistration": True, "prepareSupport": True}, - "documentLink": { - "dynamicRegistration": True, - "tooltipSupport": True, - }, - "typeDefinition": { - "dynamicRegistration": True, - "linkSupport": True, - }, - "implementation": { - "dynamicRegistration": True, - "linkSupport": True, - }, - "colorProvider": {"dynamicRegistration": True}, - "foldingRange": { - "dynamicRegistration": True, - "rangeLimit": 5000, - "lineFoldingOnly": True, - }, - "declaration": {"dynamicRegistration": True, "linkSupport": True}, - "selectionRange": {"dynamicRegistration": True}, - }, - "window": {"workDoneProgress": True}, - }, - "trace": "verbose", - "workspaceFolders": [{"uri": as_uri(str(TEST_DATA)), "name": "my_project"}], - "initializationOptions": get_initialization_options(), -} + "trace": "verbose", + "workspaceFolders": [ + {"uri": as_uri(str(PROJECT_ROOT)), "name": "my_project"} + ], + "initializationOptions": get_initialization_options(), + } + ) diff --git a/src/test/python_tests/lsp_test_client/session.py b/src/test/python_tests/lsp_test_client/session.py index f5ffc86..060577d 100644 --- a/src/test/python_tests/lsp_test_client/session.py +++ b/src/test/python_tests/lsp_test_client/session.py @@ -14,8 +14,8 @@ from pyls_jsonrpc.endpoint import Endpoint from pyls_jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter +from . import defaults from .constants import PROJECT_ROOT -from .defaults import VSCODE_DEFAULT_INITIALIZE LSP_EXIT_TIMEOUT = 5000 @@ -86,7 +86,7 @@ def initialize( ): """Sends the initialize request to LSP server.""" if initialize_params is None: - initialize_params = VSCODE_DEFAULT_INITIALIZE + initialize_params = defaults.vscode_initialize_defaults() server_initialized = Event() def _after_initialize(fut): @@ -100,7 +100,7 @@ def _after_initialize(fut): params=( initialize_params if initialize_params is not None - else VSCODE_DEFAULT_INITIALIZE + else defaults.vscode_initialize_defaults() ), handle_response=_after_initialize, ) @@ -143,7 +143,7 @@ def notify_did_close(self, did_close_params): self._send_notification("textDocument/didClose", params=did_close_params) def text_document_formatting(self, formatting_params): - """Sends text document references request to LSP server.""" + """Sends text document format request to LSP server.""" fut = self._send_request("textDocument/formatting", params=formatting_params) return fut.result() diff --git a/src/test/python_tests/lsp_test_client/utils.py b/src/test/python_tests/lsp_test_client/utils.py index 6e8ff2e..02ef434 100644 --- a/src/test/python_tests/lsp_test_client/utils.py +++ b/src/test/python_tests/lsp_test_client/utils.py @@ -3,13 +3,14 @@ """ Utility functions for use with tests. """ +import contextlib import json import os import pathlib import platform -from random import choice +import random -from .constants import PROJECT_ROOT, TEST_DATA +from .constants import PROJECT_ROOT def normalizecase(path: str) -> str: @@ -24,25 +25,18 @@ def as_uri(path: str) -> str: return normalizecase(pathlib.Path(path).as_uri()) -class PythonFile: - """Create python file on demand for testing.""" - - def __init__(self, contents, root): - self.contents = contents - self.basename = "".join( - choice("abcdefghijklmnopqrstuvwxyz") if i < 8 else ".py" for i in range(9) - ) - self.fullpath = os.path.join(root, self.basename) - - def __enter__(self): - """Creates a python file for testing.""" - with open(self.fullpath, "w", encoding="utf8") as py_file: - py_file.write(self.contents) - return self - - def __exit__(self, typ, value, _tb): - """Cleans up and deletes the python file.""" - os.unlink(self.fullpath) +@contextlib.contextmanager +def python_file(contents: str, root: pathlib.Path, ext: str = ".py"): + """Creates a temporary python file.""" + basename = ( + "".join(random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(9)) + ext + ) + fullpath = root / basename + try: + fullpath.write_text(contents) + yield fullpath + finally: + os.unlink(str(fullpath)) def get_server_info_defaults(): @@ -67,8 +61,9 @@ def get_initialization_options(): value = properties[prop]["default"] setting[name] = value - setting["workspace"] = as_uri(str(TEST_DATA)) + setting["workspace"] = as_uri(str(PROJECT_ROOT)) setting["interpreter"] = [] - setting["cwd"] = str(TEST_DATA) + setting["cwd"] = str(PROJECT_ROOT) + setting["extraPaths"] = [] - return {"settings": [setting]} + return {"settings": [setting], "globalSettings": setting} diff --git a/src/test/python_tests/test_extra_paths.py b/src/test/python_tests/test_extra_paths.py index d555673..3edb53d 100644 --- a/src/test/python_tests/test_extra_paths.py +++ b/src/test/python_tests/test_extra_paths.py @@ -1,7 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """ Test for extra paths settings. """ -import copy from threading import Event from typing import Dict @@ -38,8 +39,8 @@ def check_for_sys_paths_with_extra_paths(self, argv: Dict[str, str]): def test_extra_paths(): """Test linting using mypy with extraPaths set.""" - init_params = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_params["initializationOptions"]["settings"][0]["extraPaths"] = [ + default_init = defaults.vscode_initialize_defaults() + default_init["initializationOptions"]["settings"][0]["extraPaths"] = [ "this/is/an/extra/path", "this/is/another/extra/path", ] @@ -61,7 +62,7 @@ def _handler(_params): ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) - ls_session.initialize(init_params) + ls_session.initialize(default_init) ls_session.notify_did_open( { "textDocument": { diff --git a/src/test/python_tests/test_linting.py b/src/test/python_tests/test_linting.py index 32ad30f..d2fdb7f 100644 --- a/src/test/python_tests/test_linting.py +++ b/src/test/python_tests/test_linting.py @@ -3,10 +3,8 @@ """ Test for linting over LSP. """ - -import copy -import sys from threading import Event +from typing import List import pytest from hamcrest import assert_that, greater_than, is_ @@ -62,7 +60,7 @@ def _log_handler(params): "start": {"line": 2, "character": 6}, "end": { "line": 2, - "character": 7 if sys.version_info >= (3, 8) else 6, + "character": 7, }, }, "message": 'Name "x" is not defined', @@ -78,7 +76,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', @@ -94,7 +92,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": """This violates the Liskov substitution principle @@ -160,7 +158,7 @@ def _log_handler(params): "start": {"line": 2, "character": 6}, "end": { "line": 2, - "character": 7 if sys.version_info >= (3, 8) else 6, + "character": 7, }, }, "message": 'Name "x" is not defined', @@ -176,7 +174,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', @@ -192,7 +190,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": """This violates the Liskov substitution principle @@ -284,10 +282,10 @@ def test_severity_setting(lint_code): actual = [] with session.LspSession() as ls_session: - init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_options = init_args["initializationOptions"] + default_init = defaults.vscode_initialize_defaults() + init_options = default_init["initializationOptions"] init_options["settings"][0]["severity"][lint_code] = "Warning" - ls_session.initialize(init_args) + ls_session.initialize(default_init) done = Event() @@ -324,7 +322,7 @@ def _log_handler(params): "start": {"line": 2, "character": 6}, "end": { "line": 2, - "character": 7 if sys.version_info >= (3, 8) else 6, + "character": 7, }, }, "message": 'Name "x" is not defined', @@ -340,7 +338,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', @@ -356,7 +354,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": """This violates the Liskov substitution principle @@ -387,10 +385,19 @@ def test_workspace_reporting_scope(): actual = [] with session.LspSession() as ls_session: - init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_options = init_args["initializationOptions"] + default_init = defaults.vscode_initialize_defaults() + default_init["rootPath"] = str(constants.TEST_DATA) + default_init["rootUri"]: utils.as_uri(str(constants.TEST_DATA)) + default_init["workspaceFolders"][0]["uri"] = utils.as_uri( + str(constants.TEST_DATA) + ) + init_options = default_init["initializationOptions"] init_options["settings"][0]["reportingScope"] = "workspace" - ls_session.initialize(init_args) + init_options["settings"][0]["workspace"] = utils.as_uri( + str(constants.TEST_DATA) + ) + init_options["settings"][0]["cwd"] = str(constants.TEST_DATA) + ls_session.initialize(default_init) done = Event() @@ -429,7 +436,7 @@ def _log_handler(params): "start": {"line": 2, "character": 6}, "end": { "line": 2, - "character": 7 if sys.version_info >= (3, 8) else 6, + "character": 7, }, }, "message": 'Name "x" is not defined', @@ -445,7 +452,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', @@ -461,7 +468,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": """This violates the Liskov substitution principle @@ -488,7 +495,7 @@ def __eq__(self, other: object) -> bool: "start": {"line": 2, "character": 9}, "end": { "line": 2, - "character": 16 if sys.version_info >= (3, 8) else 9, + "character": 16, }, }, "message": 'Incompatible types in assignment (expression has type "str", variable has type "int")', @@ -559,10 +566,19 @@ def test_file_with_no_errors_generates_empty_diagnostics_workspace_mode(): actual = [] with session.LspSession() as ls_session: - init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_options = init_args["initializationOptions"] + default_init = defaults.vscode_initialize_defaults() + default_init["rootPath"] = str(constants.TEST_DATA) + default_init["rootUri"]: utils.as_uri(str(constants.TEST_DATA)) + default_init["workspaceFolders"][0]["uri"] = utils.as_uri( + str(constants.TEST_DATA) + ) + init_options = default_init["initializationOptions"] init_options["settings"][0]["reportingScope"] = "workspace" - ls_session.initialize(init_args) + init_options["settings"][0]["workspace"] = utils.as_uri( + str(constants.TEST_DATA) + ) + init_options["settings"][0]["cwd"] = str(constants.TEST_DATA) + ls_session.initialize(default_init) done = Event() @@ -601,7 +617,7 @@ def _log_handler(params): "start": {"line": 2, "character": 6}, "end": { "line": 2, - "character": 7 if sys.version_info >= (3, 8) else 6, + "character": 7, }, }, "message": 'Name "x" is not defined', @@ -617,7 +633,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', @@ -633,7 +649,7 @@ def _log_handler(params): "start": {"line": 6, "character": 21}, "end": { "line": 6, - "character": 33 if sys.version_info >= (3, 8) else 21, + "character": 33, }, }, "message": """This violates the Liskov substitution principle @@ -660,7 +676,7 @@ def __eq__(self, other: object) -> bool: "start": {"line": 2, "character": 9}, "end": { "line": 2, - "character": 16 if sys.version_info >= (3, 8) else 9, + "character": 16, }, }, "message": 'Incompatible types in assignment (expression has type "str", variable has type "int")', @@ -677,3 +693,158 @@ def __eq__(self, other: object) -> bool: # Only reports diagnostics on files that have problems assert_that(actual, is_(expected)) + + +@pytest.mark.parametrize( + "patterns", + [ + ["**/sample*.py"], + ["**/test_data/**/*.py"], + ["**/sample*.py", "**/something*.py"], + ], +) +def test_ignore_patterns_match(patterns: List[str]): + """Test to ensure linter uses the ignore pattern.""" + contents = TEST_FILE_PATH.read_text(encoding="utf-8") + + actual = [] + with session.LspSession() as ls_session: + default_init = defaults.vscode_initialize_defaults() + init_options = default_init["initializationOptions"] + init_options["settings"][0]["ignorePatterns"] = patterns + ls_session.initialize(default_init) + + done = Event() + + def _handler(params): + nonlocal actual + actual = params + done.set() + + ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) + + ls_session.notify_did_open( + { + "textDocument": { + "uri": TEST_FILE_URI, + "languageId": "python", + "version": 1, + "text": contents, + } + } + ) + + # wait for some time to receive all notifications + done.wait(TIMEOUT) + + expected = { + "uri": TEST_FILE_URI, + "diagnostics": [], + } + + assert_that(actual, is_(expected)) + + +@pytest.mark.parametrize( + "patterns", + [ + ["**/something*.py"], + ["**/something/**/*.py"], + [], + ], +) +def test_ignore_patterns_no_match(patterns: List[str]): + """Test to ensure linter uses the ignore pattern.""" + contents = TEST_FILE_PATH.read_text(encoding="utf-8") + + actual = [] + with session.LspSession() as ls_session: + default_init = defaults.vscode_initialize_defaults() + init_options = default_init["initializationOptions"] + init_options["settings"][0]["ignorePatterns"] = patterns + ls_session.initialize(default_init) + + done = Event() + + def _handler(params): + nonlocal actual + actual = params + done.set() + + ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) + + ls_session.notify_did_open( + { + "textDocument": { + "uri": TEST_FILE_URI, + "languageId": "python", + "version": 1, + "text": contents, + } + } + ) + + # wait for some time to receive all notifications + done.wait(TIMEOUT) + + expected = { + "uri": TEST_FILE_URI, + "diagnostics": [ + { + "range": { + "start": {"line": 2, "character": 6}, + "end": { + "line": 2, + "character": 7, + }, + }, + "message": 'Name "x" is not defined', + "severity": 1, + "code": "name-defined", + "codeDescription": { + "href": "https://mypy.readthedocs.io/en/latest/_refs.html#code-name-defined" + }, + "source": "Mypy", + }, + { + "range": { + "start": {"line": 6, "character": 21}, + "end": { + "line": 6, + "character": 33, + }, + }, + "message": 'Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"', + "severity": 1, + "code": "override", + "codeDescription": { + "href": "https://mypy.readthedocs.io/en/latest/_refs.html#code-override" + }, + "source": "Mypy", + }, + { + "range": { + "start": {"line": 6, "character": 21}, + "end": { + "line": 6, + "character": 33, + }, + }, + "message": """This violates the Liskov substitution principle +See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +It is recommended for "__eq__" to work with arbitrary objects, for example: + def __eq__(self, other: object) -> bool: + if not isinstance(other, Foo): + return NotImplemented + return """, + "severity": 3, + "code": "note", + "codeDescription": { + "href": "https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides" + }, + "source": "Mypy", + }, + ], + } + + assert_that(actual, is_(expected)) diff --git a/src/test/python_tests/test_non_daemon.py b/src/test/python_tests/test_non_daemon.py index 63de223..7e55501 100644 --- a/src/test/python_tests/test_non_daemon.py +++ b/src/test/python_tests/test_non_daemon.py @@ -1,4 +1,6 @@ -import copy +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +"""Tests for non-daemon mode""" from threading import Event from typing import Any, Dict, List @@ -25,15 +27,15 @@ def test_daemon_non_daemon_equivalence_for_file_with_errors(): contents = TEST_FILE1_PATH.read_text(encoding="utf-8") def run(prefer_daemon: bool): - init_params = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_params["initializationOptions"]["settings"][0][ + default_init = defaults.vscode_initialize_defaults() + default_init["initializationOptions"]["settings"][0][ "preferDaemon" ] = prefer_daemon result: Dict[str, Any] = {} log_messages: List[str] = [] with session.LspSession() as ls_session: - ls_session.initialize(init_params) + ls_session.initialize(default_init) done = Event() diff --git a/src/test/python_tests/test_path_specialization.py b/src/test/python_tests/test_path_specialization.py index 2a6ee12..bdc3584 100644 --- a/src/test/python_tests/test_path_specialization.py +++ b/src/test/python_tests/test_path_specialization.py @@ -1,7 +1,8 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """ Test for path and interpreter settings. """ -import copy import sys from threading import Event from typing import Dict @@ -36,8 +37,8 @@ def check_for_argv_duplication(self, argv: Dict[str, str]): def test_path(): """Test linting using mypy bin path set.""" - init_params = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_params["initializationOptions"]["settings"][0]["path"] = [ + default_init = defaults.vscode_initialize_defaults() + default_init["initializationOptions"]["settings"][0]["path"] = [ sys.executable, "-m", "mypy", @@ -60,7 +61,7 @@ def _handler(_params): ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) - ls_session.initialize(init_params) + ls_session.initialize(default_init) ls_session.notify_did_open( { "textDocument": { @@ -98,8 +99,8 @@ def _handler(_params): def test_interpreter(): """Test linting using specific python path.""" - init_params = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) - init_params["initializationOptions"]["settings"][0]["interpreter"] = ["python"] + default_init = defaults.vscode_initialize_defaults() + default_init["initializationOptions"]["settings"][0]["interpreter"] = ["python"] argv_callback_object = CallbackObject() contents = TEST_FILE_PATH.read_text(encoding="utf-8") @@ -118,7 +119,7 @@ def _handler(_params): ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) - ls_session.initialize(init_params) + ls_session.initialize(default_init) ls_session.notify_did_open( { "textDocument": { diff --git a/src/test/ts_tests/tests/common/settings.unit.test.ts b/src/test/ts_tests/tests/common/settings.unit.test.ts index b5a6ecf..1961168 100644 --- a/src/test/ts_tests/tests/common/settings.unit.test.ts +++ b/src/test/ts_tests/tests/common/settings.unit.test.ts @@ -71,6 +71,14 @@ suite('Settings Tests', () => { .setup((c) => c.get('showNotifications', 'off')) .returns(() => 'off') .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('cwd', workspace1.uri.fsPath)) + .returns(() => workspace1.uri.fsPath) + .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('ignorePatterns', [])) + .returns(() => []) + .verifiable(TypeMoq.Times.atLeastOnce()); pythonConfigMock .setup((c) => c.get('linting.mypyArgs', [])) @@ -96,6 +104,7 @@ suite('Settings Tests', () => { assert.deepStrictEqual(settings.showNotifications, 'off'); assert.deepStrictEqual(settings.workspace, workspace1.uri.toString()); assert.deepStrictEqual(settings.extraPaths, []); + assert.deepStrictEqual(settings.ignorePatterns, []); configMock.verifyAll(); pythonConfigMock.verifyAll(); @@ -113,6 +122,7 @@ suite('Settings Tests', () => { '${workspaceFolder}/bin/mypy', '${workspaceFolder:workspace1}/bin/mypy', '${cwd}/bin/mypy', + '${interpreter}', ]) .verifiable(TypeMoq.Times.atLeastOnce()); configMock @@ -136,6 +146,14 @@ suite('Settings Tests', () => { .setup((c) => c.get('showNotifications', 'off')) .returns(() => 'off') .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('cwd', TypeMoq.It.isAnyString())) + .returns(() => '${fileDirname}') + .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('ignorePatterns', [])) + .returns(() => []) + .verifiable(TypeMoq.Times.atLeastOnce()); pythonConfigMock .setup((c) => c.get('linting.mypyArgs', [])) @@ -157,9 +175,11 @@ suite('Settings Tests', () => { pythonConfigMock .setup((c) => c.get('linting.cwd')) .returns(() => '${userHome}/bin') - .verifiable(TypeMoq.Times.atLeastOnce()); + .verifiable(TypeMoq.Times.never()); const settings: ISettings = await getWorkspaceSettings('mypy', workspace1, true); + + assert.deepStrictEqual(settings.cwd, '${fileDirname}'); assert.deepStrictEqual(settings.args, [ process.env.HOME || process.env.USERPROFILE, workspace1.uri.fsPath, @@ -171,6 +191,10 @@ suite('Settings Tests', () => { `${workspace1.uri.fsPath}/bin/mypy`, `${workspace1.uri.fsPath}/bin/mypy`, `${process.cwd()}/bin/mypy`, + `${process.env.HOME || process.env.USERPROFILE}/bin/python`, + `${workspace1.uri.fsPath}/bin/python`, + `${workspace1.uri.fsPath}/bin/python`, + `${process.cwd()}/bin/python`, ]); assert.deepStrictEqual(settings.interpreter, [ `${process.env.HOME || process.env.USERPROFILE}/bin/python`, @@ -184,7 +208,6 @@ suite('Settings Tests', () => { `${workspace1.uri.fsPath}/lib/python`, `${process.cwd()}/lib/python`, ]); - assert.deepStrictEqual(settings.cwd, `${process.env.HOME || process.env.USERPROFILE}/bin`); configMock.verifyAll(); pythonConfigMock.verifyAll(); @@ -211,6 +234,14 @@ suite('Settings Tests', () => { .setup((c) => c.get('showNotifications', 'off')) .returns(() => 'off') .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('cwd', workspace1.uri.fsPath)) + .returns(() => '${userHome}/bin') + .verifiable(TypeMoq.Times.atLeastOnce()); + configMock + .setup((c) => c.get('ignorePatterns', [])) + .returns(() => []) + .verifiable(TypeMoq.Times.atLeastOnce()); pythonConfigMock .setup((c) => c.get('linting.mypyArgs', [])) @@ -232,7 +263,7 @@ suite('Settings Tests', () => { pythonConfigMock .setup((c) => c.get('linting.cwd')) .returns(() => '${userHome}/bin') - .verifiable(TypeMoq.Times.atLeastOnce()); + .verifiable(TypeMoq.Times.never()); const settings: ISettings = await getWorkspaceSettings('mypy', workspace1);