From ed00eac389e5bdd46816dd6ff4ffbb4db6766199 Mon Sep 17 00:00:00 2001 From: Tomasz Prus Date: Thu, 22 Feb 2024 18:39:59 +0100 Subject: [PATCH 01/22] Improve error message about missing `websockets` module (#528) --- pylsp/python_lsp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index a3f737ac..c606a7c6 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -109,7 +109,7 @@ def start_ws_lang_server(port, check_parent_process, handler_class): import websockets except ImportError as e: raise ImportError( - "websocket modules missing. Please run pip install 'python-lsp-server[websockets]" + "websocket modules missing. Please run: pip install 'python-lsp-server[websockets]'" ) from e with ThreadPoolExecutor(max_workers=10) as tpool: From 4f6d7cb5b12511d9b015eee2015bc8687bc5bc28 Mon Sep 17 00:00:00 2001 From: Serhii Tereshchenko Date: Wed, 13 Mar 2024 03:57:48 +0200 Subject: [PATCH 02/22] Fix progress reporting with autoimport plugin (#530) --- pylsp/plugins/rope_autoimport.py | 2 +- test/plugins/test_autoimport.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 1a235c3d..759f0922 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -37,7 +37,7 @@ def reload_cache( config: Config, workspace: Workspace, files: Optional[List[Document]] = None, - single_thread: Optional[bool] = False, + single_thread: Optional[bool] = True, ): if self.is_blocked(): return diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 0d1e2d73..4ac635ad 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -313,7 +313,7 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( ) assert rope_autoimport_settings.get("completions", {}).get("enabled", False) is True assert rope_autoimport_settings.get("memory", False) is True - wait_for_condition(lambda: not cache.thread.is_alive()) + wait_for_condition(lambda: not cache.is_blocked()) # 1. quick_fixes = server.code_actions("cell_1_uri", {}, make_context("os", 0, 0, 2)) From 779c97a7643fe5115e9f2eb23e223c4b4c35298d Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 12 Mar 2024 21:25:09 -0500 Subject: [PATCH 03/22] Update changelog for 1.10.1 (#534) Also, fix some small formatting issues reported by Ruff. --- CHANGELOG.md | 17 +++++++++++++++++ pylsp/_utils.py | 4 ++-- pylsp/plugins/flake8_lint.py | 1 + pylsp/plugins/pylint_lint.py | 1 + pylsp/uris.py | 1 + test/conftest.py | 3 ++- 6 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2993da97..731fe292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # History of changes +## Version 1.10.1 (2024/03/12) + +### Issues Closed + +* [Issue 529](https://github.com/python-lsp/python-lsp-server/issues/529) - Autoimports: sqlite3.OperationalError: database is locked ([PR 530](https://github.com/python-lsp/python-lsp-server/pull/530) by [@last-partizan](https://github.com/last-partizan)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 530](https://github.com/python-lsp/python-lsp-server/pull/530) - Fix progress reporting with autoimport plugin, by [@last-partizan](https://github.com/last-partizan) ([529](https://github.com/python-lsp/python-lsp-server/issues/529)) +* [PR 528](https://github.com/python-lsp/python-lsp-server/pull/528) - Improve error message about missing `websockets` module, by [@tomplus](https://github.com/tomplus) + +In this release 2 pull requests were closed. + +---- + ## Version 1.10.0 (2024/01/21) ### New features diff --git a/pylsp/_utils.py b/pylsp/_utils.py index e1d2b53c..0293ee32 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -175,8 +175,8 @@ def escape_plain_text(contents: str) -> str: """ Format plain text to display nicely in environments which do not respect whitespaces. """ - contents = contents.replace("\t", "\u00A0" * 4) - contents = contents.replace(" ", "\u00A0" * 2) + contents = contents.replace("\t", "\u00a0" * 4) + contents = contents.replace(" ", "\u00a0" * 2) return contents diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py index eed673f7..47121018 100644 --- a/pylsp/plugins/flake8_lint.py +++ b/pylsp/plugins/flake8_lint.py @@ -2,6 +2,7 @@ # Copyright 2021- Python Language Server Contributors. """Linter pluging for flake8""" + import logging import os.path import re diff --git a/pylsp/plugins/pylint_lint.py b/pylsp/plugins/pylint_lint.py index 784627b1..beffe6f3 100644 --- a/pylsp/plugins/pylint_lint.py +++ b/pylsp/plugins/pylint_lint.py @@ -3,6 +3,7 @@ # Copyright 2021- Python Language Server Contributors. """Linter plugin for pylint.""" + import collections import logging import os diff --git a/pylsp/uris.py b/pylsp/uris.py index 45ad280b..cba5b290 100644 --- a/pylsp/uris.py +++ b/pylsp/uris.py @@ -5,6 +5,7 @@ https://github.com/Microsoft/vscode-uri/blob/e59cab84f5df6265aed18ae5f43552d3eef13bb9/lib/index.ts """ + import re from urllib import parse diff --git a/test/conftest.py b/test/conftest.py index 5c4ab8f1..a9010517 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,7 +1,8 @@ # Copyright 2017-2020 Palantir Technologies, Inc. # Copyright 2021- Python Language Server Contributors. -""" py.test configuration""" +"""pytest configuration""" + import logging from pylsp.__main__ import LOG_FORMAT From 7fbec6db889eae31779d0a9fdb107e97a9decf85 Mon Sep 17 00:00:00 2001 From: justin-f-perez Date: Sun, 24 Mar 2024 13:33:03 -0500 Subject: [PATCH 04/22] Remove `.config/flake8` reference in Readme (#538) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51ec4f7e..e0e6b224 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Like all language servers, configuration can be passed from the client that talk `python-lsp-server` depends on other tools, like flake8 and pycodestyle. These tools can be configured via settings passed from the client (as above), or alternatively from other configuration sources. The following sources are available: - `pycodestyle`: discovered in `~/.config/pycodestyle`, `setup.cfg`, `tox.ini` and `pycodestyle.cfg`. -- `flake8`: discovered in `~/.config/flake8`, `.flake8`, `setup.cfg` and `tox.ini` +- `flake8`: discovered in `.flake8`, `setup.cfg` and `tox.ini` The default configuration sources are `pycodestyle` and `pyflakes`. If you would like to use `flake8`, you will need to: From 023a699060fe7cb6e168348902c1da29bfbd8b7a Mon Sep 17 00:00:00 2001 From: Piraty Date: Sun, 24 Mar 2024 18:38:34 +0000 Subject: [PATCH 05/22] Fix isort plugin name in Readme (#536) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0e6b224..85d64522 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ apk add py3-lsp-server Installing these plugins will add extra functionality to the language server: - [pylsp-mypy](https://github.com/Richardk2n/pylsp-mypy): [MyPy](http://mypy-lang.org/) type checking for Python >=3.8. -- [pyls-isort](https://github.com/chantera/python-lsp-isort): code formatting using [isort](https://github.com/PyCQA/isort) (automatic import sorting). +- [python-lsp-isort](https://github.com/chantera/python-lsp-isort): code formatting using [isort](https://github.com/PyCQA/isort) (automatic import sorting). - [python-lsp-black](https://github.com/python-lsp/python-lsp-black): code formatting using [Black](https://github.com/psf/black). - [pyls-memestra](https://github.com/QuantStack/pyls-memestra): detecting the use of deprecated APIs. - [pylsp-rope](https://github.com/python-rope/pylsp-rope): Extended refactoring capabilities using [Rope](https://github.com/python-rope/rope). From 433c6a66b110a7e3346f869d0f563b602c994322 Mon Sep 17 00:00:00 2001 From: Riley <44530786+staticf0x@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:18:46 +0100 Subject: [PATCH 06/22] Add contributing guide to setup dev environment (#470) Co-authored-by: Pavel Kulyov --- CONTRIBUTING.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..5a24957b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,40 @@ +# Setup the environment + +1. Clone the repo: `git clone git@github.com:python-lsp/python-lsp-server.git` +2. Create the virtual environment: `python -m venv .venv` +3. Activate: `source .venv/bin/activate` +4. Install an editable installation: `pip install -e .` + - This will ensure you'll see your edits immediately without reinstalling the project +5. Configure your editor to point the pylsp executable to the one in `.venv` + +## Trying out if it works + +Go to file `pylsp/python_lsp.py`, function `start_io_lang_server`, +and on the first line of the function, add some logging: + +```py +log.info("It works!") +``` + +Save the file, restart the LSP server and you should see the log line: + +``` +2023-10-12 16:46:38,320 CEST - INFO - pylsp._utils - It works! +``` + +Now the project is setup in a way you can quickly iterate change you want to add. + +# Running tests + +1. Install runtime dependencies: `pip install .[all]` +2. Install test dependencies: `pip install .[test]` +3. Run `pytest`: `pytest -v` + +## Useful pytest options + +- To run a specific test file, use `pytest test/test_utils.py` +- To run a specific test function within a test file, + use `pytest test/test_utils.py::test_debounce` +- To run tests matching a certain expression, use `pytest -k format` +- To increase verbosity of pytest, use `pytest -v` or `pytest -vv` +- To enter a debugger on failed tests, use `pytest --pdb` From 245da207e3659fdfbbf1882ef4832f94f8076a7e Mon Sep 17 00:00:00 2001 From: Savalek - Aleksey Savelenko <30798933+Savalek@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:24:35 +0300 Subject: [PATCH 07/22] Add fallback for `ujson` import (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Савеленко Алексей Николаевич --- pylsp/python_lsp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index c606a7c6..528ffdb4 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -9,7 +9,11 @@ from functools import partial from typing import Any, Dict, List -import ujson as json +try: + import ujson as json +except Exception: + import json + from pylsp_jsonrpc.dispatchers import MethodDispatcher from pylsp_jsonrpc.endpoint import Endpoint from pylsp_jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter From f69ed849a922a3e6a21e4b365704a393007f95d4 Mon Sep 17 00:00:00 2001 From: Niall Dooley Date: Fri, 29 Mar 2024 16:30:48 +0100 Subject: [PATCH 08/22] Remove built-in `rope_rename` plugin (#515) --- pylsp/plugins/jedi_rename.py | 2 +- pylsp/plugins/rope_rename.py | 66 ----------------------------- pyproject.toml | 1 - test/plugins/test_rope_rename.py | 73 -------------------------------- 4 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 pylsp/plugins/rope_rename.py delete mode 100644 test/plugins/test_rope_rename.py diff --git a/pylsp/plugins/jedi_rename.py b/pylsp/plugins/jedi_rename.py index 5606a881..b35e321a 100644 --- a/pylsp/plugins/jedi_rename.py +++ b/pylsp/plugins/jedi_rename.py @@ -20,7 +20,7 @@ def pylsp_rename(config, workspace, document, position, new_name): except NotImplementedError as exc: raise Exception( "No support for renaming in Python 2/3.5 with Jedi. " - "Consider using the rope_rename plugin instead" + "Consider using the pylsp-rope plugin instead" ) from exc log.debug("Finished rename: %s", refactoring.get_diff()) changes = [] diff --git a/pylsp/plugins/rope_rename.py b/pylsp/plugins/rope_rename.py deleted file mode 100644 index 9e386944..00000000 --- a/pylsp/plugins/rope_rename.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2017-2020 Palantir Technologies, Inc. -# Copyright 2021- Python Language Server Contributors. - -import logging - -from rope.base import libutils -from rope.refactor.rename import Rename - -from pylsp import _utils, hookimpl, uris - -log = logging.getLogger(__name__) - - -@hookimpl -def pylsp_settings(): - # Default rope_rename to disabled - return {"plugins": {"rope_rename": {"enabled": False}}} - - -@hookimpl -def pylsp_rename(config, workspace, document, position, new_name): - rope_config = config.settings(document_path=document.path).get("rope", {}) - rope_project = workspace._rope_project_builder(rope_config) - - rename = Rename( - rope_project, - libutils.path_to_resource(rope_project, document.path), - document.offset_at_position(position), - ) - - log.debug( - "Executing rename of %s to %s", document.word_at_position(position), new_name - ) - changeset = rename.get_changes(new_name, in_hierarchy=True, docs=True) - log.debug("Finished rename: %s", changeset.changes) - changes = [] - for change in changeset.changes: - uri = uris.from_fs_path(change.resource.path) - doc = workspace.get_maybe_document(uri) - changes.append( - { - "textDocument": {"uri": uri, "version": doc.version if doc else None}, - "edits": [ - { - "range": { - "start": {"line": 0, "character": 0}, - "end": { - "line": _num_lines(change.resource), - "character": 0, - }, - }, - "newText": change.new_contents, - } - ], - } - ) - return {"documentChanges": changes} - - -def _num_lines(resource): - "Count the number of lines in a `File` resource." - text = resource.read() - - if _utils.get_eol_chars(text): - return len(text.splitlines()) - return 0 diff --git a/pyproject.toml b/pyproject.toml index 4665dcbe..9dbb4bbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,7 +79,6 @@ pydocstyle = "pylsp.plugins.pydocstyle_lint" pyflakes = "pylsp.plugins.pyflakes_lint" pylint = "pylsp.plugins.pylint_lint" rope_completion = "pylsp.plugins.rope_completion" -rope_rename = "pylsp.plugins.rope_rename" rope_autoimport = "pylsp.plugins.rope_autoimport" yapf = "pylsp.plugins.yapf_format" diff --git a/test/plugins/test_rope_rename.py b/test/plugins/test_rope_rename.py deleted file mode 100644 index c55ead0a..00000000 --- a/test/plugins/test_rope_rename.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2017-2020 Palantir Technologies, Inc. -# Copyright 2021- Python Language Server Contributors. - -import os - -import pytest - -from pylsp import uris -from pylsp.plugins.rope_rename import pylsp_rename -from pylsp.workspace import Document - -DOC_NAME = "test1.py" -DOC = """class Test1(): - pass - -class Test2(Test1): - pass -""" - -DOC_NAME_SIMPLE = "test2.py" -DOC_SIMPLE = "foo = 12" - - -@pytest.fixture -def tmp_workspace(temp_workspace_factory): - return temp_workspace_factory({DOC_NAME: DOC, DOC_NAME_SIMPLE: DOC_SIMPLE}) - - -def test_rope_rename(tmp_workspace, config): - position = {"line": 0, "character": 6} - DOC_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC_NAME)) - doc = Document(DOC_URI, tmp_workspace) - - result = pylsp_rename(config, tmp_workspace, doc, position, "ShouldBeRenamed") - assert len(result.keys()) == 1 - - changes = result.get("documentChanges") - assert len(changes) == 1 - changes = changes[0] - - # Note that this test differs from test_jedi_rename, because rope does not - # seem to modify files that haven't been opened with textDocument/didOpen. - assert changes.get("edits") == [ - { - "range": { - "start": {"line": 0, "character": 0}, - "end": {"line": 5, "character": 0}, - }, - "newText": "class ShouldBeRenamed():\n pass\n\nclass Test2(ShouldBeRenamed):\n pass\n", - } - ] - - # Regression test for issue python-lsp/python-lsp-server#413 - # rename foo - position = {"line": 0, "character": 0} - DOC_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC_NAME_SIMPLE)) - doc = Document(DOC_URI, tmp_workspace) - - result = pylsp_rename(config, tmp_workspace, doc, position, "bar") - assert len(result.keys()) == 1 - - changes = result.get("documentChanges") - assert len(changes) == 1 - - assert changes[0].get("edits") == [ - { - "range": { - "start": {"line": 0, "character": 0}, - "end": {"line": 0, "character": 0}, - }, - "newText": "bar = 12", - } - ] From 803bb886a13ce92b7dd8ca3c1b936c7b29939c54 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Fri, 29 Mar 2024 16:38:08 +0100 Subject: [PATCH 09/22] Bump pylint to `>=3.1,<4` (#543) --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9dbb4bbd..f68cc2ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ all = [ "pycodestyle>=2.11.0,<2.12.0", "pydocstyle>=6.3.0,<6.4.0", "pyflakes>=3.2.0,<3.3.0", - "pylint>=2.5.0,<3.1", + "pylint>=3.1,<4", "rope>=1.11.0", "yapf>=0.33.0", "whatthepatch>=1.0.2,<2.0.0" @@ -44,12 +44,12 @@ mccabe = ["mccabe>=0.7.0,<0.8.0"] pycodestyle = ["pycodestyle>=2.11.0,<2.12.0"] pydocstyle = ["pydocstyle>=6.3.0,<6.4.0"] pyflakes = ["pyflakes>=3.2.0,<3.3.0"] -pylint = ["pylint>=2.5.0,<3.1"] +pylint = ["pylint>=3.1,<4"] rope = ["rope>=1.11.0"] yapf = ["yapf>=0.33.0", "whatthepatch>=1.0.2,<2.0.0"] websockets = ["websockets>=10.3"] test = [ - "pylint>=2.5.0,<3.1", + "pylint>=3.1,<4", "pytest", "pytest-cov", "coverage", From 4c0e99b41ed39c514d0173fde72f7ac9d147662b Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 29 Mar 2024 10:54:51 -0500 Subject: [PATCH 10/22] Update changelog for 1.11.0 (#544) --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 731fe292..dcc85065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # History of changes +## Version 1.11.0 (2024/03/29) + +### New features + +* Remove the `rope_rename` plugin. People that were using it need to install + the `pylsp-rope` third-party plugin instead. +* Add support for Pylint 3.1 + +### Issues Closed + +* [Issue 255](https://github.com/python-lsp/python-lsp-server/issues/255) - Confusion about rename support ([PR 515](https://github.com/python-lsp/python-lsp-server/pull/515) by [@doolio](https://github.com/doolio)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 543](https://github.com/python-lsp/python-lsp-server/pull/543) - Bump pylint to `>=3.1,<4`, by [@bnavigator](https://github.com/bnavigator) +* [PR 541](https://github.com/python-lsp/python-lsp-server/pull/541) - Add fallback for `ujson` import, by [@Savalek](https://github.com/Savalek) +* [PR 538](https://github.com/python-lsp/python-lsp-server/pull/538) - Remove `.config/flake8` reference in Readme, by [@justin-f-perez](https://github.com/justin-f-perez) +* [PR 536](https://github.com/python-lsp/python-lsp-server/pull/536) - Fix isort plugin name in Readme, by [@Piraty](https://github.com/Piraty) +* [PR 515](https://github.com/python-lsp/python-lsp-server/pull/515) - Remove built-in `rope_rename` plugin, by [@doolio](https://github.com/doolio) ([255](https://github.com/python-lsp/python-lsp-server/issues/255)) +* [PR 470](https://github.com/python-lsp/python-lsp-server/pull/470) - Add contributing guide to setup dev environment, by [@staticf0x](https://github.com/staticf0x) + +In this release 6 pull requests were closed. + +---- + ## Version 1.10.1 (2024/03/12) ### Issues Closed From dd313289f1ebcbaa4c67c345b69b51730b7b1e27 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 18 May 2024 16:27:34 -0500 Subject: [PATCH 11/22] Use macOS 13 to run our tests on CI (#560) --- .github/workflows/test-mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-mac.yml b/.github/workflows/test-mac.yml index 1021ccf0..79df9b29 100644 --- a/.github/workflows/test-mac.yml +++ b/.github/workflows/test-mac.yml @@ -16,7 +16,7 @@ concurrency: jobs: build: name: Mac Py${{ matrix.PYTHON_VERSION }} - runs-on: macos-latest + runs-on: macos-13 env: CI: 'true' OS: 'macos' From 457c5c00f64b5f31a23a9590e27e26e7eed5915e Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 24 Jul 2024 10:50:36 -0500 Subject: [PATCH 12/22] Fix linting issues reported by the latest version of Ruff (#585) --- pylsp/config/source.py | 8 ++++---- test/fixtures.py | 2 +- test/plugins/test_autoimport.py | 4 ++-- test/test_configuration.py | 4 ++-- test/test_document.py | 3 +-- test/test_language_server.py | 3 ++- test/test_notebook_document.py | 10 +++++----- test/test_uris.py | 3 +-- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/pylsp/config/source.py b/pylsp/config/source.py index c82bfaeb..da455857 100644 --- a/pylsp/config/source.py +++ b/pylsp/config/source.py @@ -53,16 +53,16 @@ def _get_opt(cls, config, key, option, opt_type): if not config.has_option(key, opt_key): continue - if opt_type == bool: + if opt_type is bool: return config.getboolean(key, opt_key) - if opt_type == int: + if opt_type is int: return config.getint(key, opt_key) - if opt_type == str: + if opt_type is str: return config.get(key, opt_key) - if opt_type == list: + if opt_type is list: return cls._parse_list_opt(config.get(key, opt_key)) raise ValueError("Unknown option type: %s" % opt_type) diff --git a/test/fixtures.py b/test/fixtures.py index 81a8b082..2bce8269 100644 --- a/test/fixtures.py +++ b/test/fixtures.py @@ -3,7 +3,6 @@ import os from io import StringIO -from test.test_utils import CALL_TIMEOUT_IN_SECONDS, ClientServerPair from unittest.mock import MagicMock import pytest @@ -15,6 +14,7 @@ from pylsp.config.config import Config from pylsp.python_lsp import PythonLSPServer from pylsp.workspace import Document, Workspace +from test.test_utils import CALL_TIMEOUT_IN_SECONDS, ClientServerPair DOC_URI = uris.from_fs_path(__file__) DOC = """import sys diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 4ac635ad..9a4d0e70 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -1,7 +1,5 @@ # Copyright 2022- Python Language Server Contributors. -from test.test_notebook_document import wait_for_condition -from test.test_utils import send_initialize_request, send_notebook_did_open from typing import Any, Dict, List from unittest.mock import Mock, patch @@ -22,6 +20,8 @@ pylsp_completions as pylsp_autoimport_completions, ) from pylsp.workspace import Workspace +from test.test_notebook_document import wait_for_condition +from test.test_utils import send_initialize_request, send_notebook_did_open DOC_URI = uris.from_fs_path(__file__) diff --git a/test/test_configuration.py b/test/test_configuration.py index ddc6315d..a6ebaacc 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -1,12 +1,12 @@ # Copyright 2021- Python Language Server Contributors. -from test.test_notebook_document import wait_for_condition -from test.test_utils import send_initialize_request from unittest.mock import patch import pytest from pylsp import IS_WIN +from test.test_notebook_document import wait_for_condition +from test.test_utils import send_initialize_request INITIALIZATION_OPTIONS = { "pylsp": { diff --git a/test/test_document.py b/test/test_document.py index 7caa0abb..dd7b7828 100644 --- a/test/test_document.py +++ b/test/test_document.py @@ -1,9 +1,8 @@ # Copyright 2017-2020 Palantir Technologies, Inc. # Copyright 2021- Python Language Server Contributors. -from test.fixtures import DOC, DOC_URI - from pylsp.workspace import Document +from test.fixtures import DOC, DOC_URI def test_document_props(doc): diff --git a/test/test_language_server.py b/test/test_language_server.py index 6a48638f..6d806f93 100644 --- a/test/test_language_server.py +++ b/test/test_language_server.py @@ -4,12 +4,13 @@ import os import sys import time -from test.test_utils import ClientServerPair, send_initialize_request import pytest from flaky import flaky from pylsp_jsonrpc.exceptions import JsonRpcMethodNotFound +from test.test_utils import ClientServerPair, send_initialize_request + RUNNING_IN_CI = bool(os.environ.get("CI")) CALL_TIMEOUT_IN_SECONDS = 10 diff --git a/test/test_notebook_document.py b/test/test_notebook_document.py index c1ac1986..d1d3ddc5 100644 --- a/test/test_notebook_document.py +++ b/test/test_notebook_document.py @@ -1,11 +1,6 @@ # Copyright 2021- Python Language Server Contributors. import time -from test.test_utils import ( - CALL_TIMEOUT_IN_SECONDS, - send_initialize_request, - send_notebook_did_open, -) from unittest.mock import call, patch import pytest @@ -13,6 +8,11 @@ from pylsp import IS_WIN from pylsp.lsp import NotebookCellKind from pylsp.workspace import Notebook +from test.test_utils import ( + CALL_TIMEOUT_IN_SECONDS, + send_initialize_request, + send_notebook_did_open, +) def wait_for_condition(condition, timeout=CALL_TIMEOUT_IN_SECONDS): diff --git a/test/test_uris.py b/test/test_uris.py index f00973a4..e418ef56 100644 --- a/test/test_uris.py +++ b/test/test_uris.py @@ -1,11 +1,10 @@ # Copyright 2017-2020 Palantir Technologies, Inc. # Copyright 2021- Python Language Server Contributors. -from test import unix_only, windows_only - import pytest from pylsp import uris +from test import unix_only, windows_only @unix_only From 8cb1af2add7614305f47c33b57882b3f2837b5bb Mon Sep 17 00:00:00 2001 From: Sandro Date: Wed, 24 Jul 2024 17:52:47 +0200 Subject: [PATCH 13/22] Fix Fedora instructions (#570) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85d64522..4cf305cc 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ sudo apt-get install python3-pylsp or Fedora Linux ``` -sudo dnf install python-lsp-server +sudo dnf install python3-lsp-server ``` or Arch Linux From 7901a7ca57962df33f5049571dc13243b4c47ecd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 24 Jul 2024 11:16:10 -0500 Subject: [PATCH 14/22] Update versions of Github actions used on CI (#586) --- .github/workflows/static.yml | 6 +++--- .github/workflows/test-linux.yml | 6 +++--- .github/workflows/test-mac.yml | 6 +++--- .github/workflows/test-win.yml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index d5fd459d..3bba01c8 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -22,13 +22,13 @@ jobs: OS: 'linux' timeout-minutes: 2 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: static-pip-${{ hashFiles('pyproject.toml') }} restore-keys: static-pip- - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: # TODO: check with Python 3, but need to fix the # errors first diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 700d09f7..89277d67 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -27,13 +27,13 @@ jobs: PYTHON_VERSION: ['3.10', '3.9', '3.8'] timeout-minutes: 10 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' diff --git a/.github/workflows/test-mac.yml b/.github/workflows/test-mac.yml index 79df9b29..d9e4818f 100644 --- a/.github/workflows/test-mac.yml +++ b/.github/workflows/test-mac.yml @@ -27,13 +27,13 @@ jobs: PYTHON_VERSION: ['3.10', '3.9', '3.8'] timeout-minutes: 10 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~/Library/Caches/pip key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index b541acc0..1db41154 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -27,13 +27,13 @@ jobs: PYTHON_VERSION: ['3.10', '3.9', '3.8'] timeout-minutes: 10 steps: - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~\AppData\Local\pip\Cache key: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-${{ matrix.PYTHON_VERSION }}-pip- - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.PYTHON_VERSION }} architecture: 'x64' From a02339d5925ffae5b841add552ac8419b71520c7 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Wed, 24 Jul 2024 18:19:23 +0200 Subject: [PATCH 15/22] Bump flake8 to 7.1 (#576) --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f68cc2ed..f9c6a521 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,9 +28,9 @@ Homepage = "https://github.com/python-lsp/python-lsp-server" [project.optional-dependencies] all = [ "autopep8>=2.0.4,<2.1.0", - "flake8>=7,<8", + "flake8>=7.1,<8", "mccabe>=0.7.0,<0.8.0", - "pycodestyle>=2.11.0,<2.12.0", + "pycodestyle>=2.12.0,<2.13.0", "pydocstyle>=6.3.0,<6.4.0", "pyflakes>=3.2.0,<3.3.0", "pylint>=3.1,<4", @@ -39,9 +39,9 @@ all = [ "whatthepatch>=1.0.2,<2.0.0" ] autopep8 = ["autopep8>=2.0.4,<2.1.0"] -flake8 = ["flake8>=7,<8"] +flake8 = ["flake8>=7.1,<8"] mccabe = ["mccabe>=0.7.0,<0.8.0"] -pycodestyle = ["pycodestyle>=2.11.0,<2.12.0"] +pycodestyle = ["pycodestyle>=2.12.0,<2.13.0"] pydocstyle = ["pydocstyle>=6.3.0,<6.4.0"] pyflakes = ["pyflakes>=3.2.0,<3.3.0"] pylint = ["pylint>=3.1,<4"] From cf3cea11c17bdf1bf87046a2fd835d325202566b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Nogueras?= Date: Wed, 24 Jul 2024 18:22:39 +0200 Subject: [PATCH 16/22] Add `extendSelect` option to flake8 plugin (#559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Saúl Nogueras --- CONFIGURATION.md | 1 + pylsp/config/flake8_conf.py | 1 + pylsp/config/schema.json | 10 +++++++++- pylsp/plugins/flake8_lint.py | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index acf8a85f..bb07fce9 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -9,6 +9,7 @@ This server can be configured using the `workspace/didChangeConfiguration` metho | `pylsp.plugins.flake8.enabled` | `boolean` | Enable or disable the plugin. | `false` | | `pylsp.plugins.flake8.exclude` | `array` of `string` items | List of files or directories to exclude. | `[]` | | `pylsp.plugins.flake8.extendIgnore` | `array` of `string` items | List of errors and warnings to append to ignore list. | `[]` | +| `pylsp.plugins.flake8.extendSelect` | `array` of `string` items | List of errors and warnings to append to select list. | `[]` | | `pylsp.plugins.flake8.executable` | `string` | Path to the flake8 executable. | `"flake8"` | | `pylsp.plugins.flake8.filename` | `string` | Only check for filenames matching the patterns in this list. | `null` | | `pylsp.plugins.flake8.hangClosing` | `boolean` | Hang closing bracket instead of matching indentation of opening bracket's line. | `null` | diff --git a/pylsp/config/flake8_conf.py b/pylsp/config/flake8_conf.py index 5e969d97..74258709 100644 --- a/pylsp/config/flake8_conf.py +++ b/pylsp/config/flake8_conf.py @@ -27,6 +27,7 @@ # flake8 ("exclude", "plugins.flake8.exclude", list), ("extend-ignore", "plugins.flake8.extendIgnore", list), + ("extend-select", "plugins.flake8.extendSelect", list), ("filename", "plugins.flake8.filename", list), ("hang-closing", "plugins.flake8.hangClosing", bool), ("ignore", "plugins.flake8.ignore", list), diff --git a/pylsp/config/schema.json b/pylsp/config/schema.json index ba1d36f8..2259f1cc 100644 --- a/pylsp/config/schema.json +++ b/pylsp/config/schema.json @@ -53,6 +53,14 @@ }, "description": "List of errors and warnings to append to ignore list." }, + "pylsp.plugins.flake8.extendSelect": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "description": "List of errors and warnings to append to select list." + }, "pylsp.plugins.flake8.executable": { "type": "string", "default": "flake8", @@ -500,4 +508,4 @@ "description": "The name of the folder in which rope stores project configurations and data. Pass `null` for not using such a folder at all." } } -} \ No newline at end of file +} diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py index 47121018..74e2664c 100644 --- a/pylsp/plugins/flake8_lint.py +++ b/pylsp/plugins/flake8_lint.py @@ -72,6 +72,7 @@ def pylsp_lint(workspace, document): "config": settings.get("config"), "exclude": settings.get("exclude"), "extend-ignore": settings.get("extendIgnore"), + "extend-select": settings.get("extendSelect"), "filename": settings.get("filename"), "hang-closing": settings.get("hangClosing"), "ignore": ignores or None, From 9b79576131146703e5dabae0ab38ef4203792cc6 Mon Sep 17 00:00:00 2001 From: Dylan Mayor <36193755+Dylmay@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:37:38 +0100 Subject: [PATCH 17/22] Add version support to `workspace/publishDiagnostics` (#565) --- pylsp/python_lsp.py | 6 +++--- pylsp/workspace.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index 528ffdb4..8606aba1 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -442,13 +442,13 @@ def lint(self, doc_uri, is_saved): workspace = self._match_uri_to_workspace(doc_uri) document_object = workspace.documents.get(doc_uri, None) if isinstance(document_object, Document): - self._lint_text_document(doc_uri, workspace, is_saved=is_saved) + self._lint_text_document(doc_uri, workspace, is_saved, document_object.version) elif isinstance(document_object, Notebook): self._lint_notebook_document(document_object, workspace) - def _lint_text_document(self, doc_uri, workspace, is_saved): + def _lint_text_document(self, doc_uri, workspace, is_saved, doc_version=None): workspace.publish_diagnostics( - doc_uri, flatten(self._hook("pylsp_lint", doc_uri, is_saved=is_saved)) + doc_uri, flatten(self._hook("pylsp_lint", doc_uri, is_saved=is_saved)), doc_version, ) def _lint_notebook_document(self, notebook_document, workspace): diff --git a/pylsp/workspace.py b/pylsp/workspace.py index c1b32f20..f047ed08 100644 --- a/pylsp/workspace.py +++ b/pylsp/workspace.py @@ -176,10 +176,18 @@ def update_config(self, settings): def apply_edit(self, edit): return self._endpoint.request(self.M_APPLY_EDIT, {"edit": edit}) - def publish_diagnostics(self, doc_uri, diagnostics): + def publish_diagnostics(self, doc_uri, diagnostics, doc_version=None): + params = { + "uri": doc_uri, + "diagnostics": diagnostics, + } + + if doc_version: + params["version"] = doc_version + self._endpoint.notify( self.M_PUBLISH_DIAGNOSTICS, - params={"uri": doc_uri, "diagnostics": diagnostics}, + params=params, ) @contextmanager From e8dd582ca038d47e41c0d82022fe387dd714350e Mon Sep 17 00:00:00 2001 From: Dylan Mayor <36193755+Dylmay@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:54:56 +0100 Subject: [PATCH 18/22] Add `window/logMessage` support (#573) --- pylsp/python_lsp.py | 8 ++++++-- pylsp/workspace.py | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index 8606aba1..d50d5242 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -442,13 +442,17 @@ def lint(self, doc_uri, is_saved): workspace = self._match_uri_to_workspace(doc_uri) document_object = workspace.documents.get(doc_uri, None) if isinstance(document_object, Document): - self._lint_text_document(doc_uri, workspace, is_saved, document_object.version) + self._lint_text_document( + doc_uri, workspace, is_saved, document_object.version + ) elif isinstance(document_object, Notebook): self._lint_notebook_document(document_object, workspace) def _lint_text_document(self, doc_uri, workspace, is_saved, doc_version=None): workspace.publish_diagnostics( - doc_uri, flatten(self._hook("pylsp_lint", doc_uri, is_saved=is_saved)), doc_version, + doc_uri, + flatten(self._hook("pylsp_lint", doc_uri, is_saved=is_saved)), + doc_version, ) def _lint_notebook_document(self, notebook_document, workspace): diff --git a/pylsp/workspace.py b/pylsp/workspace.py index f047ed08..c9b63a99 100644 --- a/pylsp/workspace.py +++ b/pylsp/workspace.py @@ -41,6 +41,7 @@ class Workspace: M_INITIALIZE_PROGRESS = "window/workDoneProgress/create" M_APPLY_EDIT = "workspace/applyEdit" M_SHOW_MESSAGE = "window/showMessage" + M_LOG_MESSAGE = "window/logMessage" def __init__(self, root_uri, endpoint, config=None): self._config = config @@ -324,6 +325,11 @@ def _progress_end(self, token: str, message: Optional[str] = None) -> None: }, ) + def log_message(self, message, msg_type=lsp.MessageType.Info): + self._endpoint.notify( + self.M_LOG_MESSAGE, params={"type": msg_type, "message": message} + ) + def show_message(self, message, msg_type=lsp.MessageType.Info): self._endpoint.notify( self.M_SHOW_MESSAGE, params={"type": msg_type, "message": message} From 8a345b9eccc06b8377623905b7c2277efd548e30 Mon Sep 17 00:00:00 2001 From: Miki Tebeka Date: Mon, 29 Jul 2024 20:55:12 +0300 Subject: [PATCH 19/22] Use `%r` to have a better log (#584) --- pylsp/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 0293ee32..1be7e263 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -88,7 +88,7 @@ def find_parents(root, path, names): return [] if not os.path.commonprefix((root, path)): - log.warning("Path %s not in %s", path, root) + log.warning("Path %r not in %r", path, root) return [] # Split the relative by directory, generate all the parent directories, then check each of them. From cabac8e6f941520d7d61e47feb6dda4cfa20811f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20Garc=C3=ADa?= <133214663+agserrano3@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:11:29 +0200 Subject: [PATCH 20/22] Set return type to `None` for functions without returns (#581) Co-authored-by: codegen-bot --- pylsp/__main__.py | 6 ++-- pylsp/config/config.py | 6 ++-- pylsp/config/source.py | 6 ++-- pylsp/hookspecs.py | 50 +++++++++++++------------- pylsp/plugins/_resolvers.py | 4 +-- pylsp/plugins/_rope_task_handle.py | 6 ++-- pylsp/plugins/preload_imports.py | 2 +- pylsp/plugins/pycodestyle_lint.py | 2 +- pylsp/plugins/pydocstyle_lint.py | 2 +- pylsp/plugins/pyflakes_lint.py | 8 ++--- pylsp/plugins/rope_autoimport.py | 14 ++++---- pylsp/python_lsp.py | 46 ++++++++++++------------ pylsp/workspace.py | 34 +++++++++--------- scripts/jsonschema2md.py | 2 +- test/fixtures.py | 4 +-- test/plugins/test_autoimport.py | 52 ++++++++++++++------------- test/plugins/test_autopep8_format.py | 10 +++--- test/plugins/test_completion.py | 50 +++++++++++++------------- test/plugins/test_definitions.py | 14 ++++---- test/plugins/test_flake8_lint.py | 16 ++++----- test/plugins/test_folding.py | 4 +-- test/plugins/test_highlight.py | 4 +-- test/plugins/test_hover.py | 6 ++-- test/plugins/test_jedi_rename.py | 2 +- test/plugins/test_mccabe_lint.py | 4 +-- test/plugins/test_pycodestyle_lint.py | 6 ++-- test/plugins/test_pydocstyle_lint.py | 8 ++--- test/plugins/test_pyflakes_lint.py | 8 ++--- test/plugins/test_pylint_lint.py | 14 ++++---- test/plugins/test_references.py | 4 +-- test/plugins/test_signature.py | 8 ++--- test/plugins/test_symbols.py | 6 ++-- test/plugins/test_yapf_format.py | 18 +++++----- test/test_configuration.py | 6 ++-- test/test_document.py | 18 +++++----- test/test_language_server.py | 10 +++--- test/test_notebook_document.py | 16 ++++----- test/test_text_edit.py | 8 ++--- test/test_uris.py | 10 +++--- test/test_utils.py | 24 +++++++------ test/test_workspace.py | 44 +++++++++++------------ 41 files changed, 287 insertions(+), 275 deletions(-) diff --git a/pylsp/__main__.py b/pylsp/__main__.py index 61bd015b..44aa3cfa 100644 --- a/pylsp/__main__.py +++ b/pylsp/__main__.py @@ -25,7 +25,7 @@ ) -def add_arguments(parser): +def add_arguments(parser) -> None: parser.description = "Python Language Server" parser.add_argument( @@ -67,7 +67,7 @@ def add_arguments(parser): ) -def main(): +def main() -> None: parser = argparse.ArgumentParser() add_arguments(parser) args = parser.parse_args() @@ -94,7 +94,7 @@ def _binary_stdio(): return stdin, stdout -def _configure_logger(verbose=0, log_config=None, log_file=None): +def _configure_logger(verbose=0, log_config=None, log_file=None) -> None: root_logger = logging.root if log_config: diff --git a/pylsp/config/config.py b/pylsp/config/config.py index 0d9f2635..815f8fd2 100644 --- a/pylsp/config/config.py +++ b/pylsp/config/config.py @@ -43,7 +43,7 @@ def _hookexec( class Config: - def __init__(self, root_uri, init_opts, process_id, capabilities): + def __init__(self, root_uri, init_opts, process_id, capabilities) -> None: self._root_path = uris.to_fs_path(root_uri) self._root_uri = root_uri self._init_opts = init_opts @@ -185,14 +185,14 @@ def plugin_settings(self, plugin, document_path=None): .get(plugin, {}) ) - def update(self, settings): + def update(self, settings) -> None: """Recursively merge the given settings into the current settings.""" self.settings.cache_clear() self._settings = settings log.info("Updated settings to %s", self._settings) self._update_disabled_plugins() - def _update_disabled_plugins(self): + def _update_disabled_plugins(self) -> None: # All plugins default to enabled self._disabled_plugins = [ plugin diff --git a/pylsp/config/source.py b/pylsp/config/source.py index da455857..8ffc8b71 100644 --- a/pylsp/config/source.py +++ b/pylsp/config/source.py @@ -12,18 +12,18 @@ class ConfigSource: """Base class for implementing a config source.""" - def __init__(self, root_path): + def __init__(self, root_path) -> None: self.root_path = root_path self.is_windows = sys.platform == "win32" self.xdg_home = os.environ.get( "XDG_CONFIG_HOME", os.path.expanduser("~/.config") ) - def user_config(self): + def user_config(self) -> None: """Return user-level (i.e. home directory) configuration.""" raise NotImplementedError() - def project_config(self, document_path): + def project_config(self, document_path) -> None: """Return project-level (i.e. workspace directory) configuration.""" raise NotImplementedError() diff --git a/pylsp/hookspecs.py b/pylsp/hookspecs.py index a2549fbc..41508be1 100644 --- a/pylsp/hookspecs.py +++ b/pylsp/hookspecs.py @@ -10,12 +10,12 @@ def pylsp_code_actions(config, workspace, document, range, context): @hookspec -def pylsp_code_lens(config, workspace, document): +def pylsp_code_lens(config, workspace, document) -> None: pass @hookspec -def pylsp_commands(config, workspace): +def pylsp_commands(config, workspace) -> None: """The list of command strings supported by the server. Returns: @@ -24,110 +24,112 @@ def pylsp_commands(config, workspace): @hookspec -def pylsp_completions(config, workspace, document, position, ignored_names): +def pylsp_completions(config, workspace, document, position, ignored_names) -> None: pass @hookspec(firstresult=True) -def pylsp_completion_item_resolve(config, workspace, document, completion_item): +def pylsp_completion_item_resolve(config, workspace, document, completion_item) -> None: pass @hookspec -def pylsp_definitions(config, workspace, document, position): +def pylsp_definitions(config, workspace, document, position) -> None: pass @hookspec -def pylsp_dispatchers(config, workspace): +def pylsp_dispatchers(config, workspace) -> None: pass @hookspec -def pylsp_document_did_open(config, workspace, document): +def pylsp_document_did_open(config, workspace, document) -> None: pass @hookspec -def pylsp_document_did_save(config, workspace, document): +def pylsp_document_did_save(config, workspace, document) -> None: pass @hookspec -def pylsp_document_highlight(config, workspace, document, position): +def pylsp_document_highlight(config, workspace, document, position) -> None: pass @hookspec -def pylsp_document_symbols(config, workspace, document): +def pylsp_document_symbols(config, workspace, document) -> None: pass @hookspec(firstresult=True) -def pylsp_execute_command(config, workspace, command, arguments): +def pylsp_execute_command(config, workspace, command, arguments) -> None: pass @hookspec -def pylsp_experimental_capabilities(config, workspace): +def pylsp_experimental_capabilities(config, workspace) -> None: pass @hookspec -def pylsp_folding_range(config, workspace, document): +def pylsp_folding_range(config, workspace, document) -> None: pass @hookspec(firstresult=True) -def pylsp_format_document(config, workspace, document, options): +def pylsp_format_document(config, workspace, document, options) -> None: pass @hookspec(firstresult=True) -def pylsp_format_range(config, workspace, document, range, options): +def pylsp_format_range(config, workspace, document, range, options) -> None: pass @hookspec(firstresult=True) -def pylsp_hover(config, workspace, document, position): +def pylsp_hover(config, workspace, document, position) -> None: pass @hookspec -def pylsp_initialize(config, workspace): +def pylsp_initialize(config, workspace) -> None: pass @hookspec -def pylsp_initialized(): +def pylsp_initialized() -> None: pass @hookspec -def pylsp_lint(config, workspace, document, is_saved): +def pylsp_lint(config, workspace, document, is_saved) -> None: pass @hookspec -def pylsp_references(config, workspace, document, position, exclude_declaration): +def pylsp_references( + config, workspace, document, position, exclude_declaration +) -> None: pass @hookspec(firstresult=True) -def pylsp_rename(config, workspace, document, position, new_name): +def pylsp_rename(config, workspace, document, position, new_name) -> None: pass @hookspec -def pylsp_settings(config): +def pylsp_settings(config) -> None: pass @hookspec(firstresult=True) -def pylsp_signature_help(config, workspace, document, position): +def pylsp_signature_help(config, workspace, document, position) -> None: pass @hookspec -def pylsp_workspace_configuration_changed(config, workspace): +def pylsp_workspace_configuration_changed(config, workspace) -> None: pass diff --git a/pylsp/plugins/_resolvers.py b/pylsp/plugins/_resolvers.py index 835b3aff..44d6d882 100644 --- a/pylsp/plugins/_resolvers.py +++ b/pylsp/plugins/_resolvers.py @@ -15,7 +15,7 @@ # ---- Base class # ----------------------------------------------------------------------------- class Resolver: - def __init__(self, callback, resolve_on_error, time_to_live=60 * 30): + def __init__(self, callback, resolve_on_error, time_to_live=60 * 30) -> None: self.callback = callback self.resolve_on_error = resolve_on_error self._cache = {} @@ -33,7 +33,7 @@ def cached_modules(self): def cached_modules(self, new_value): self._cached_modules = set(new_value) - def clear_outdated(self): + def clear_outdated(self) -> None: now = self.time_key() to_clear = [timestamp for timestamp in self._cache_ttl if timestamp < now] for time_key in to_clear: diff --git a/pylsp/plugins/_rope_task_handle.py b/pylsp/plugins/_rope_task_handle.py index a72ef56e..8bc13c1d 100644 --- a/pylsp/plugins/_rope_task_handle.py +++ b/pylsp/plugins/_rope_task_handle.py @@ -19,7 +19,7 @@ class PylspJobSet(BaseJobSet): _report_iter: ContextManager job_name: str = "" - def __init__(self, count: Optional[int], report_iter: ContextManager): + def __init__(self, count: Optional[int], report_iter: ContextManager) -> None: if count is not None: self.count = count self._reporter = report_iter.__enter__() @@ -57,7 +57,7 @@ def increment(self) -> None: self._report() @throttle(0.5) - def _report(self): + def _report(self) -> None: percent = int(self.get_percent_done()) message = f"{self.job_name} {self.done}/{self.count}" log.debug(f"Reporting {message} {percent}%") @@ -72,7 +72,7 @@ class PylspTaskHandle(BaseTaskHandle): workspace: Workspace _report: Callable[[str, str], None] - def __init__(self, workspace: Workspace): + def __init__(self, workspace: Workspace) -> None: self.workspace = workspace self.job_sets = [] self.observers = [] diff --git a/pylsp/plugins/preload_imports.py b/pylsp/plugins/preload_imports.py index a45a6666..ebcd9adb 100644 --- a/pylsp/plugins/preload_imports.py +++ b/pylsp/plugins/preload_imports.py @@ -67,7 +67,7 @@ def pylsp_settings(): @hookimpl -def pylsp_initialize(config): +def pylsp_initialize(config) -> None: for mod_name in config.plugin_settings("preload").get("modules", []): try: __import__(mod_name) diff --git a/pylsp/plugins/pycodestyle_lint.py b/pylsp/plugins/pycodestyle_lint.py index 62b0b8ad..7a514adf 100644 --- a/pylsp/plugins/pycodestyle_lint.py +++ b/pylsp/plugins/pycodestyle_lint.py @@ -65,7 +65,7 @@ def pylsp_lint(workspace, document): class PyCodeStyleDiagnosticReport(pycodestyle.BaseReport): - def __init__(self, options): + def __init__(self, options) -> None: self.diagnostics = [] super().__init__(options=options) diff --git a/pylsp/plugins/pydocstyle_lint.py b/pylsp/plugins/pydocstyle_lint.py index 7d04bdf0..a310ac84 100644 --- a/pylsp/plugins/pydocstyle_lint.py +++ b/pylsp/plugins/pydocstyle_lint.py @@ -115,7 +115,7 @@ def _parse_diagnostic(document, error): @contextlib.contextmanager -def _patch_sys_argv(arguments): +def _patch_sys_argv(arguments) -> None: old_args = sys.argv # Preserve argv[0] since it's the executable diff --git a/pylsp/plugins/pyflakes_lint.py b/pylsp/plugins/pyflakes_lint.py index e23f9a0f..8a04276c 100644 --- a/pylsp/plugins/pyflakes_lint.py +++ b/pylsp/plugins/pyflakes_lint.py @@ -32,11 +32,11 @@ def pylsp_lint(workspace, document): class PyflakesDiagnosticReport: - def __init__(self, lines): + def __init__(self, lines) -> None: self.lines = lines self.diagnostics = [] - def unexpectedError(self, _filename, msg): # pragma: no cover + def unexpectedError(self, _filename, msg) -> None: # pragma: no cover err_range = { "start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}, @@ -50,7 +50,7 @@ def unexpectedError(self, _filename, msg): # pragma: no cover } ) - def syntaxError(self, _filename, msg, lineno, offset, text): + def syntaxError(self, _filename, msg, lineno, offset, text) -> None: # We've seen that lineno and offset can sometimes be None lineno = lineno or 1 offset = offset or 0 @@ -71,7 +71,7 @@ def syntaxError(self, _filename, msg, lineno, offset, text): } ) - def flake(self, message): + def flake(self, message) -> None: """Get message like :: """ err_range = { "start": {"line": message.lineno - 1, "character": message.col}, diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 759f0922..12f5d80b 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -29,7 +29,7 @@ class AutoimportCache: """Handles the cache creation.""" - def __init__(self): + def __init__(self) -> None: self.thread = None def reload_cache( @@ -66,7 +66,7 @@ def _reload_cache( workspace: Workspace, autoimport: AutoImport, resources: Optional[List[Resource]] = None, - ): + ) -> None: task_handle = PylspTaskHandle(workspace) autoimport.generate_cache(task_handle=task_handle, resources=resources) autoimport.generate_modules_cache(task_handle=task_handle) @@ -365,7 +365,7 @@ def pylsp_code_actions( @hookimpl -def pylsp_initialize(config: Config, workspace: Workspace): +def pylsp_initialize(config: Config, workspace: Workspace) -> None: """Initialize AutoImport. Generates the cache for local and global items. @@ -374,7 +374,7 @@ def pylsp_initialize(config: Config, workspace: Workspace): @hookimpl -def pylsp_document_did_open(config: Config, workspace: Workspace): +def pylsp_document_did_open(config: Config, workspace: Workspace) -> None: """Initialize AutoImport. Generates the cache for local and global items. @@ -383,13 +383,15 @@ def pylsp_document_did_open(config: Config, workspace: Workspace): @hookimpl -def pylsp_document_did_save(config: Config, workspace: Workspace, document: Document): +def pylsp_document_did_save( + config: Config, workspace: Workspace, document: Document +) -> None: """Update the names associated with this document.""" cache.reload_cache(config, workspace, [document]) @hookimpl -def pylsp_workspace_configuration_changed(config: Config, workspace: Workspace): +def pylsp_workspace_configuration_changed(config: Config, workspace: Workspace) -> None: """ Initialize autoimport if it has been enabled through a workspace/didChangeConfiguration message from the frontend. diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index d50d5242..ba41d6aa 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -38,11 +38,11 @@ class _StreamHandlerWrapper(socketserver.StreamRequestHandler): delegate = None - def setup(self): + def setup(self) -> None: super().setup() self.delegate = self.DELEGATE_CLASS(self.rfile, self.wfile) - def handle(self): + def handle(self) -> None: try: self.delegate.start() except OSError as e: @@ -55,7 +55,7 @@ def handle(self): self.SHUTDOWN_CALL() -def start_tcp_lang_server(bind_addr, port, check_parent_process, handler_class): +def start_tcp_lang_server(bind_addr, port, check_parent_process, handler_class) -> None: if not issubclass(handler_class, PythonLSPServer): raise ValueError("Handler class must be an instance of PythonLSPServer") @@ -93,7 +93,7 @@ def shutdown_server(check_parent_process, *args): server.server_close() -def start_io_lang_server(rfile, wfile, check_parent_process, handler_class): +def start_io_lang_server(rfile, wfile, check_parent_process, handler_class) -> None: if not issubclass(handler_class, PythonLSPServer): raise ValueError("Handler class must be an instance of PythonLSPServer") log.info("Starting %s IO language server", handler_class.__name__) @@ -101,7 +101,7 @@ def start_io_lang_server(rfile, wfile, check_parent_process, handler_class): server.start() -def start_ws_lang_server(port, check_parent_process, handler_class): +def start_ws_lang_server(port, check_parent_process, handler_class) -> None: if not issubclass(handler_class, PythonLSPServer): raise ValueError("Handler class must be an instance of PythonLSPServer") @@ -165,7 +165,7 @@ class PythonLSPServer(MethodDispatcher): def __init__( self, rx, tx, check_parent_process=False, consumer=None, *, endpoint_cls=None - ): + ) -> None: self.workspace = None self.config = None self.root_uri = None @@ -198,11 +198,11 @@ def __init__( self._dispatchers = [] self._shutdown = False - def start(self): + def start(self) -> None: """Entry point for the server.""" self._jsonrpc_stream_reader.listen(self._endpoint.consume) - def consume(self, message): + def consume(self, message) -> None: """Entry point for consumer based server. Alternative to stream listeners.""" # assuming message will be JSON self._endpoint.consume(message) @@ -226,7 +226,7 @@ def __getitem__(self, item): raise KeyError() - def m_shutdown(self, **_kwargs): + def m_shutdown(self, **_kwargs) -> None: for workspace in self.workspaces.values(): workspace.close() self._shutdown = True @@ -239,7 +239,7 @@ def m_invalid_request_after_shutdown(self, **_kwargs): } } - def m_exit(self, **_kwargs): + def m_exit(self, **_kwargs) -> None: self._endpoint.shutdown() if self._jsonrpc_stream_reader is not None: self._jsonrpc_stream_reader.close() @@ -379,7 +379,7 @@ def watch_parent_process(pid): }, } - def m_initialized(self, **_kwargs): + def m_initialized(self, **_kwargs) -> None: self._hook("pylsp_initialized") def code_actions(self, doc_uri: str, range: Dict, context: Dict): @@ -437,7 +437,7 @@ def hover(self, doc_uri, position): return self._hook("pylsp_hover", doc_uri, position=position) or {"contents": ""} @_utils.debounce(LINT_DEBOUNCE_S, keyed_by="doc_uri") - def lint(self, doc_uri, is_saved): + def lint(self, doc_uri, is_saved) -> None: # Since we're debounced, the document may no longer be open workspace = self._match_uri_to_workspace(doc_uri) document_object = workspace.documents.get(doc_uri, None) @@ -448,14 +448,16 @@ def lint(self, doc_uri, is_saved): elif isinstance(document_object, Notebook): self._lint_notebook_document(document_object, workspace) - def _lint_text_document(self, doc_uri, workspace, is_saved, doc_version=None): + def _lint_text_document( + self, doc_uri, workspace, is_saved, doc_version=None + ) -> None: workspace.publish_diagnostics( doc_uri, flatten(self._hook("pylsp_lint", doc_uri, is_saved=is_saved)), doc_version, ) - def _lint_notebook_document(self, notebook_document, workspace): + def _lint_notebook_document(self, notebook_document, workspace) -> None: """ Lint a notebook document. @@ -545,7 +547,7 @@ def m_completion_item__resolve(self, **completionItem): def m_notebook_document__did_open( self, notebookDocument=None, cellTextDocuments=None, **_kwargs - ): + ) -> None: workspace = self._match_uri_to_workspace(notebookDocument["uri"]) workspace.put_notebook_document( notebookDocument["uri"], @@ -566,7 +568,7 @@ def m_notebook_document__did_open( def m_notebook_document__did_close( self, notebookDocument=None, cellTextDocuments=None, **_kwargs - ): + ) -> None: workspace = self._match_uri_to_workspace(notebookDocument["uri"]) for cell in cellTextDocuments or []: workspace.publish_diagnostics(cell["uri"], []) @@ -575,7 +577,7 @@ def m_notebook_document__did_close( def m_notebook_document__did_change( self, notebookDocument=None, change=None, **_kwargs - ): + ) -> None: """ Changes to the notebook document. @@ -649,12 +651,12 @@ def m_notebook_document__did_change( workspace.update_document(cell_uri, cell["changes"][0]) self.lint(notebookDocument["uri"], is_saved=True) - def m_text_document__did_close(self, textDocument=None, **_kwargs): + def m_text_document__did_close(self, textDocument=None, **_kwargs) -> None: workspace = self._match_uri_to_workspace(textDocument["uri"]) workspace.publish_diagnostics(textDocument["uri"], []) workspace.rm_document(textDocument["uri"]) - def m_text_document__did_open(self, textDocument=None, **_kwargs): + def m_text_document__did_open(self, textDocument=None, **_kwargs) -> None: workspace = self._match_uri_to_workspace(textDocument["uri"]) workspace.put_document( textDocument["uri"], @@ -666,7 +668,7 @@ def m_text_document__did_open(self, textDocument=None, **_kwargs): def m_text_document__did_change( self, contentChanges=None, textDocument=None, **_kwargs - ): + ) -> None: workspace = self._match_uri_to_workspace(textDocument["uri"]) for change in contentChanges: workspace.update_document( @@ -674,7 +676,7 @@ def m_text_document__did_change( ) self.lint(textDocument["uri"], is_saved=False) - def m_text_document__did_save(self, textDocument=None, **_kwargs): + def m_text_document__did_save(self, textDocument=None, **_kwargs) -> None: self.lint(textDocument["uri"], is_saved=True) self.document_did_save(textDocument["uri"]) @@ -798,7 +800,7 @@ def m_text_document__signature_help( ): return self.signature_help(textDocument["uri"], position) - def m_workspace__did_change_configuration(self, settings=None): + def m_workspace__did_change_configuration(self, settings=None) -> None: if self.config is not None: self.config.update((settings or {}).get("pylsp", {})) for workspace in self.workspaces.values(): diff --git a/pylsp/workspace.py b/pylsp/workspace.py index c9b63a99..139028cc 100644 --- a/pylsp/workspace.py +++ b/pylsp/workspace.py @@ -43,7 +43,7 @@ class Workspace: M_SHOW_MESSAGE = "window/showMessage" M_LOG_MESSAGE = "window/logMessage" - def __init__(self, root_uri, endpoint, config=None): + def __init__(self, root_uri, endpoint, config=None) -> None: self._config = config self._root_uri = root_uri self._endpoint = endpoint @@ -119,20 +119,20 @@ def get_cell_document(self, doc_uri): def get_maybe_document(self, doc_uri): return self._docs.get(doc_uri) - def put_document(self, doc_uri, source, version=None): + def put_document(self, doc_uri, source, version=None) -> None: self._docs[doc_uri] = self._create_document( doc_uri, source=source, version=version ) def put_notebook_document( self, doc_uri, notebook_type, cells, version=None, metadata=None - ): + ) -> None: self._docs[doc_uri] = self._create_notebook_document( doc_uri, notebook_type, cells, version, metadata ) @contextmanager - def temp_document(self, source, path=None): + def temp_document(self, source, path=None) -> None: if path is None: path = self.root_path uri = uris.from_fs_path(os.path.join(path, str(uuid.uuid4()))) @@ -142,26 +142,26 @@ def temp_document(self, source, path=None): finally: self.rm_document(uri) - def add_notebook_cells(self, doc_uri, cells, start): + def add_notebook_cells(self, doc_uri, cells, start) -> None: self._docs[doc_uri].add_cells(cells, start) - def remove_notebook_cells(self, doc_uri, start, delete_count): + def remove_notebook_cells(self, doc_uri, start, delete_count) -> None: self._docs[doc_uri].remove_cells(start, delete_count) - def update_notebook_metadata(self, doc_uri, metadata): + def update_notebook_metadata(self, doc_uri, metadata) -> None: self._docs[doc_uri].metadata = metadata def put_cell_document( self, doc_uri, notebook_uri, language_id, source, version=None - ): + ) -> None: self._docs[doc_uri] = self._create_cell_document( doc_uri, notebook_uri, language_id, source, version ) - def rm_document(self, doc_uri): + def rm_document(self, doc_uri) -> None: self._docs.pop(doc_uri) - def update_document(self, doc_uri, change, version=None): + def update_document(self, doc_uri, change, version=None) -> None: self._docs[doc_uri].apply_change(change) self._docs[doc_uri].version = version @@ -177,7 +177,7 @@ def update_config(self, settings): def apply_edit(self, edit): return self._endpoint.request(self.M_APPLY_EDIT, {"edit": edit}) - def publish_diagnostics(self, doc_uri, diagnostics, doc_version=None): + def publish_diagnostics(self, doc_uri, diagnostics, doc_version=None) -> None: params = { "uri": doc_uri, "diagnostics": diagnostics, @@ -330,7 +330,7 @@ def log_message(self, message, msg_type=lsp.MessageType.Info): self.M_LOG_MESSAGE, params={"type": msg_type, "message": message} ) - def show_message(self, message, msg_type=lsp.MessageType.Info): + def show_message(self, message, msg_type=lsp.MessageType.Info) -> None: self._endpoint.notify( self.M_SHOW_MESSAGE, params={"type": msg_type, "message": message} ) @@ -386,7 +386,7 @@ def _create_cell_document( rope_project_builder=self._rope_project_builder, ) - def close(self): + def close(self) -> None: if self.__rope_autoimport: self.__rope_autoimport.close() @@ -401,7 +401,7 @@ def __init__( local=True, extra_sys_path=None, rope_project_builder=None, - ): + ) -> None: self.uri = uri self.version = version self.path = uris.to_fs_path(uri) @@ -440,7 +440,7 @@ def source(self): return f.read() return self._source - def update_config(self, settings): + def update_config(self, settings) -> None: self._config.update((settings or {}).get("pylsp", {})) @lock @@ -600,7 +600,7 @@ class Notebook: def __init__( self, uri, notebook_type, workspace, cells=None, version=None, metadata=None - ): + ) -> None: self.uri = uri self.notebook_type = notebook_type self.workspace = workspace @@ -685,7 +685,7 @@ def __init__( local=True, extra_sys_path=None, rope_project_builder=None, - ): + ) -> None: super().__init__( uri, workspace, source, version, local, extra_sys_path, rope_project_builder ) diff --git a/scripts/jsonschema2md.py b/scripts/jsonschema2md.py index 10b7f855..c0a00759 100644 --- a/scripts/jsonschema2md.py +++ b/scripts/jsonschema2md.py @@ -73,7 +73,7 @@ def convert_schema(schema: dict, source: str = None) -> str: return "\n".join(lines) -def main(argv): +def main(argv) -> None: parser = ArgumentParser() parser.add_argument("schema", type=FileType()) parser.add_argument("markdown", type=FileType("w+"), default=sys.stdout) diff --git a/test/fixtures.py b/test/fixtures.py index 2bce8269..dd10140c 100644 --- a/test/fixtures.py +++ b/test/fixtures.py @@ -112,7 +112,7 @@ class Dispatcher(FakeEditorMethodsMixin, MethodDispatcher): @pytest.fixture -def workspace(tmpdir, endpoint): +def workspace(tmpdir, endpoint) -> None: """Return a workspace.""" ws = Workspace(uris.from_fs_path(str(tmpdir)), endpoint) ws._config = Config(ws.root_uri, {}, 0, {}) @@ -166,7 +166,7 @@ def create_file(name, content): @pytest.fixture -def client_server_pair(): +def client_server_pair() -> None: """A fixture that sets up a client/server pair and shuts down the server""" client_server_pair_obj = ClientServerPair() diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 9a4d0e70..dbad8d02 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -61,7 +61,7 @@ def autoimport_workspace(tmp_path_factory) -> Workspace: @pytest.fixture -def completions(config: Config, autoimport_workspace: Workspace, request): +def completions(config: Config, autoimport_workspace: Workspace, request) -> None: document, position = request.param com_position = {"line": 0, "character": position} autoimport_workspace.put_document(DOC_URI, source=document) @@ -86,7 +86,7 @@ def check_dict(query: Dict, results: List[Dict]) -> bool: @pytest.mark.parametrize("completions", [("""pathli """, 6)], indirect=True) -def test_autoimport_completion(completions): +def test_autoimport_completion(completions) -> None: assert completions assert check_dict( {"label": "pathlib", "kind": lsp.CompletionItemKind.Module}, completions @@ -94,12 +94,12 @@ def test_autoimport_completion(completions): @pytest.mark.parametrize("completions", [("""import """, 7)], indirect=True) -def test_autoimport_import(completions): +def test_autoimport_import(completions) -> None: assert len(completions) == 0 @pytest.mark.parametrize("completions", [("""pathlib""", 2)], indirect=True) -def test_autoimport_pathlib(completions): +def test_autoimport_pathlib(completions) -> None: assert completions[0]["label"] == "pathlib" start = {"line": 0, "character": 0} @@ -110,41 +110,41 @@ def test_autoimport_pathlib(completions): @pytest.mark.parametrize("completions", [("""import test\n""", 10)], indirect=True) -def test_autoimport_import_with_name(completions): +def test_autoimport_import_with_name(completions) -> None: assert len(completions) == 0 @pytest.mark.parametrize("completions", [("""def func(s""", 10)], indirect=True) -def test_autoimport_function(completions): +def test_autoimport_function(completions) -> None: assert len(completions) == 0 @pytest.mark.parametrize("completions", [("""class Test""", 10)], indirect=True) -def test_autoimport_class(completions): +def test_autoimport_class(completions) -> None: assert len(completions) == 0 @pytest.mark.parametrize("completions", [("""\n""", 0)], indirect=True) -def test_autoimport_empty_line(completions): +def test_autoimport_empty_line(completions) -> None: assert len(completions) == 0 @pytest.mark.parametrize( "completions", [("""class Test(NamedTupl):""", 20)], indirect=True ) -def test_autoimport_class_complete(completions): +def test_autoimport_class_complete(completions) -> None: assert len(completions) > 0 @pytest.mark.parametrize( "completions", [("""class Test(NamedTupl""", 20)], indirect=True ) -def test_autoimport_class_incomplete(completions): +def test_autoimport_class_incomplete(completions) -> None: assert len(completions) > 0 @pytest.mark.parametrize("completions", [("""def func(s:Lis""", 12)], indirect=True) -def test_autoimport_function_typing(completions): +def test_autoimport_function_typing(completions) -> None: assert len(completions) > 0 assert check_dict({"label": "List"}, completions) @@ -152,7 +152,7 @@ def test_autoimport_function_typing(completions): @pytest.mark.parametrize( "completions", [("""def func(s : Lis ):""", 16)], indirect=True ) -def test_autoimport_function_typing_complete(completions): +def test_autoimport_function_typing_complete(completions) -> None: assert len(completions) > 0 assert check_dict({"label": "List"}, completions) @@ -160,12 +160,12 @@ def test_autoimport_function_typing_complete(completions): @pytest.mark.parametrize( "completions", [("""def func(s : Lis ) -> Generat:""", 29)], indirect=True ) -def test_autoimport_function_typing_return(completions): +def test_autoimport_function_typing_return(completions) -> None: assert len(completions) > 0 assert check_dict({"label": "Generator"}, completions) -def test_autoimport_defined_name(config, workspace): +def test_autoimport_defined_name(config, workspace) -> None: document = """List = "hi"\nLis""" com_position = {"line": 1, "character": 3} workspace.put_document(DOC_URI, source=document) @@ -178,30 +178,30 @@ def test_autoimport_defined_name(config, workspace): class TestShouldInsert: - def test_dot(self): + def test_dot(self) -> None: assert not should_insert("""str.""", 4) - def test_dot_partial(self): + def test_dot_partial(self) -> None: assert not should_insert("""str.metho\n""", 9) - def test_comment(self): + def test_comment(self) -> None: assert not should_insert("""#""", 1) - def test_comment_indent(self): + def test_comment_indent(self) -> None: assert not should_insert(""" # """, 5) - def test_from(self): + def test_from(self) -> None: assert not should_insert("""from """, 5) assert should_insert("""from """, 4) -def test_sort_sources(): +def test_sort_sources() -> None: result1 = _get_score(1, "import pathlib", "pathlib", "pathli") result2 = _get_score(2, "import pathlib", "pathlib", "pathli") assert result1 < result2 -def test_sort_statements(): +def test_sort_statements() -> None: result1 = _get_score( 2, "from importlib_metadata import pathlib", "pathlib", "pathli" ) @@ -209,7 +209,7 @@ def test_sort_statements(): assert result1 > result2 -def test_sort_both(): +def test_sort_both() -> None: result1 = _get_score( 3, "from importlib_metadata import pathlib", "pathlib", "pathli" ) @@ -217,7 +217,7 @@ def test_sort_both(): assert result1 > result2 -def test_get_names(): +def test_get_names() -> None: source = """ from a import s as e import blah, bleh @@ -237,7 +237,9 @@ class sfa: "message", ["Undefined name `os`", "F821 undefined name 'numpy'", "undefined name 'numpy'"], ) -def test_autoimport_code_actions_get_correct_module_name(autoimport_workspace, message): +def test_autoimport_code_actions_get_correct_module_name( + autoimport_workspace, message +) -> None: source = "os.path.join('a', 'b')" autoimport_workspace.put_document(DOC_URI, source=source) doc = autoimport_workspace.get_document(DOC_URI) @@ -274,7 +276,7 @@ def position(line, character): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") def test_autoimport_code_actions_and_completions_for_notebook_document( client_server_pair, -): +) -> None: client, server = client_server_pair send_initialize_request( client, diff --git a/test/plugins/test_autopep8_format.py b/test/plugins/test_autopep8_format.py index ecdf2419..4966b89d 100644 --- a/test/plugins/test_autopep8_format.py +++ b/test/plugins/test_autopep8_format.py @@ -39,7 +39,7 @@ def func(): """ -def test_format(config, workspace): +def test_format(config, workspace) -> None: doc = Document(DOC_URI, workspace, DOC) res = pylsp_format_document(config, workspace, doc, options=None) @@ -47,7 +47,7 @@ def test_format(config, workspace): assert res[0]["newText"] == "a = 123\n\n\ndef func():\n pass\n" -def test_range_format(config, workspace): +def test_range_format(config, workspace) -> None: doc = Document(DOC_URI, workspace, DOC) def_range = { @@ -62,12 +62,12 @@ def test_range_format(config, workspace): assert res[0]["newText"] == "a = 123\n\n\n\n\ndef func():\n pass\n" -def test_no_change(config, workspace): +def test_no_change(config, workspace) -> None: doc = Document(DOC_URI, workspace, GOOD_DOC) assert not pylsp_format_document(config, workspace, doc, options=None) -def test_hanging_indentation(config, workspace): +def test_hanging_indentation(config, workspace) -> None: doc = Document(DOC_URI, workspace, INDENTED_DOC) res = pylsp_format_document(config, workspace, doc, options=None) @@ -76,7 +76,7 @@ def test_hanging_indentation(config, workspace): @pytest.mark.parametrize("newline", ["\r\n", "\r"]) -def test_line_endings(config, workspace, newline): +def test_line_endings(config, workspace, newline) -> None: doc = Document(DOC_URI, workspace, f"import os;import sys{2 * newline}dict(a=1)") res = pylsp_format_document(config, workspace, doc, options=None) diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index 4b598be8..d1ca5ef8 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -51,7 +51,7 @@ def documented_hello(): """ -def test_rope_import_completion(config, workspace): +def test_rope_import_completion(config, workspace) -> None: com_position = {"line": 0, "character": 7} doc = Document(DOC_URI, workspace, DOC) items = pylsp_rope_completions(config, workspace, doc, com_position) @@ -131,7 +131,7 @@ def test_jedi_completion_type(case, config, workspace): assert items[case.label]["kind"] == case.expected -def test_jedi_completion(config, workspace): +def test_jedi_completion(config, workspace) -> None: # Over 'i' in os.path.isabs(...) com_position = {"line": 1, "character": 15} doc = Document(DOC_URI, workspace, DOC) @@ -145,7 +145,7 @@ def test_jedi_completion(config, workspace): pylsp_jedi_completions(config, doc, {"line": 1, "character": 1000}) -def test_jedi_completion_item_resolve(config, workspace): +def test_jedi_completion_item_resolve(config, workspace) -> None: # Over the blank line com_position = {"line": 8, "character": 0} doc = Document(DOC_URI, workspace, DOC) @@ -169,7 +169,7 @@ def test_jedi_completion_item_resolve(config, workspace): assert resolved_documented_hello["documentation"] == expected_doc -def test_jedi_completion_with_fuzzy_enabled(config, workspace): +def test_jedi_completion_with_fuzzy_enabled(config, workspace) -> None: # Over 'i' in os.path.isabs(...) config.update({"plugins": {"jedi_completion": {"fuzzy": True}}}) com_position = {"line": 1, "character": 15} @@ -188,7 +188,7 @@ def test_jedi_completion_with_fuzzy_enabled(config, workspace): pylsp_jedi_completions(config, doc, {"line": 1, "character": 1000}) -def test_jedi_completion_resolve_at_most(config, workspace): +def test_jedi_completion_resolve_at_most(config, workspace) -> None: # Over 'i' in os.path.isabs(...) com_position = {"line": 1, "character": 15} doc = Document(DOC_URI, workspace, DOC) @@ -206,7 +206,7 @@ def test_jedi_completion_resolve_at_most(config, workspace): assert "isfile(path)" in labels -def test_rope_completion(config, workspace): +def test_rope_completion(config, workspace) -> None: # Over 'i' in os.path.isabs(...) com_position = {"line": 1, "character": 15} workspace.put_document(DOC_URI, source=DOC) @@ -217,7 +217,7 @@ def test_rope_completion(config, workspace): assert items[0]["label"] == "isabs" -def test_jedi_completion_ordering(config, workspace): +def test_jedi_completion_ordering(config, workspace) -> None: # Over the blank line com_position = {"line": 8, "character": 0} doc = Document(DOC_URI, workspace, DOC) @@ -230,7 +230,7 @@ def test_jedi_completion_ordering(config, workspace): assert items["hello()"] < items["_a_hello()"] -def test_jedi_property_completion(config, workspace): +def test_jedi_property_completion(config, workspace) -> None: # Over the 'w' in 'print Hello().world' com_position = {"line": 18, "character": 15} doc = Document(DOC_URI, workspace, DOC) @@ -242,7 +242,7 @@ def test_jedi_property_completion(config, workspace): assert "world" in list(items.keys())[0] -def test_jedi_method_completion(config, workspace): +def test_jedi_method_completion(config, workspace) -> None: # Over the 'y' in 'print Hello().every' com_position = {"line": 20, "character": 19} doc = Document(DOC_URI, workspace, DOC) @@ -281,7 +281,7 @@ def test_jedi_method_completion(config, workspace): PY2 or (sys.platform.startswith("linux") and os.environ.get("CI") is not None), reason="Test in Python 3 and not on CIs on Linux because wheels don't work on them.", ) -def test_pyqt_completion(config, workspace): +def test_pyqt_completion(config, workspace) -> None: # Over 'QA' in 'from PyQt5.QtWidgets import QApplication' doc_pyqt = "from PyQt5.QtWidgets import QA" com_position = {"line": 0, "character": len(doc_pyqt)} @@ -291,7 +291,7 @@ def test_pyqt_completion(config, workspace): assert completions is not None -def test_numpy_completions(config, workspace): +def test_numpy_completions(config, workspace) -> None: doc_numpy = "import numpy as np; np." com_position = {"line": 0, "character": len(doc_numpy)} doc = Document(DOC_URI, workspace, doc_numpy) @@ -301,7 +301,7 @@ def test_numpy_completions(config, workspace): assert any("array" in i["label"] for i in items) -def test_pandas_completions(config, workspace): +def test_pandas_completions(config, workspace) -> None: doc_pandas = "import pandas as pd; pd." com_position = {"line": 0, "character": len(doc_pandas)} doc = Document(DOC_URI, workspace, doc_pandas) @@ -311,7 +311,7 @@ def test_pandas_completions(config, workspace): assert any("DataFrame" in i["label"] for i in items) -def test_matplotlib_completions(config, workspace): +def test_matplotlib_completions(config, workspace) -> None: doc_mpl = "import matplotlib.pyplot as plt; plt." com_position = {"line": 0, "character": len(doc_mpl)} doc = Document(DOC_URI, workspace, doc_mpl) @@ -321,7 +321,7 @@ def test_matplotlib_completions(config, workspace): assert any("plot" in i["label"] for i in items) -def test_snippets_completion(config, workspace): +def test_snippets_completion(config, workspace) -> None: doc_snippets = "from collections import defaultdict \na=defaultdict" com_position = {"line": 0, "character": 35} doc = Document(DOC_URI, workspace, doc_snippets) @@ -338,7 +338,7 @@ def test_snippets_completion(config, workspace): assert completions[0]["insertTextFormat"] == lsp.InsertTextFormat.Snippet -def test_snippets_completion_at_most(config, workspace): +def test_snippets_completion_at_most(config, workspace) -> None: doc_snippets = "from collections import defaultdict \na=defaultdict" doc = Document(DOC_URI, workspace, doc_snippets) config.capabilities["textDocument"] = { @@ -353,7 +353,7 @@ def test_snippets_completion_at_most(config, workspace): assert not completions[0].get("insertTextFormat", None) -def test_completion_with_class_objects(config, workspace): +def test_completion_with_class_objects(config, workspace) -> None: doc_text = "class FOOBAR(Object): pass\nFOOB" com_position = {"line": 1, "character": 4} doc = Document(DOC_URI, workspace, doc_text) @@ -380,7 +380,7 @@ def test_completion_with_class_objects(config, workspace): assert completions[1]["kind"] == lsp.CompletionItemKind.TypeParameter -def test_completion_with_function_objects(config, workspace): +def test_completion_with_function_objects(config, workspace) -> None: doc_text = "def foobar(): pass\nfoob" com_position = {"line": 1, "character": 4} doc = Document(DOC_URI, workspace, doc_text) @@ -407,7 +407,7 @@ def test_completion_with_function_objects(config, workspace): assert completions[1]["kind"] == lsp.CompletionItemKind.TypeParameter -def test_snippet_parsing(config, workspace): +def test_snippet_parsing(config, workspace) -> None: doc = "divmod" completion_position = {"line": 0, "character": 6} doc = Document(DOC_URI, workspace, doc) @@ -423,7 +423,7 @@ def test_snippet_parsing(config, workspace): assert completions[0]["insertText"] == out -def test_multiline_import_snippets(config, workspace): +def test_multiline_import_snippets(config, workspace) -> None: document = "from datetime import(\n date,\n datetime)\na=date" doc = Document(DOC_URI, workspace, document) config.capabilities["textDocument"] = { @@ -440,7 +440,7 @@ def test_multiline_import_snippets(config, workspace): assert completions[0]["insertText"] == "datetime" -def test_multiline_snippets(config, workspace): +def test_multiline_snippets(config, workspace) -> None: document = "from datetime import\\\n date,\\\n datetime \na=date" doc = Document(DOC_URI, workspace, document) config.capabilities["textDocument"] = { @@ -457,7 +457,7 @@ def test_multiline_snippets(config, workspace): assert completions[0]["insertText"] == "datetime" -def test_multistatement_snippet(config, workspace): +def test_multistatement_snippet(config, workspace) -> None: config.capabilities["textDocument"] = { "completion": {"completionItem": {"snippetSupport": True}} } @@ -476,7 +476,7 @@ def test_multistatement_snippet(config, workspace): assert completions[0]["insertText"] == "fmod(${1:x}, ${2:y})$0" -def test_jedi_completion_extra_paths(tmpdir, workspace): +def test_jedi_completion_extra_paths(tmpdir, workspace) -> None: # Create a tempfile with some content and pass to extra_paths temp_doc_content = """ def spam(): @@ -510,7 +510,7 @@ def spam(): @pytest.mark.skipif( PY2 or not LINUX or not CI, reason="tested on linux and python 3 only" ) -def test_jedi_completion_environment(workspace): +def test_jedi_completion_environment(workspace) -> None: # Content of doc to test completion doc_content = """import logh """ @@ -539,7 +539,7 @@ def test_jedi_completion_environment(workspace): assert "changelog generator" in resolved["documentation"]["value"].lower() -def test_document_path_completions(tmpdir, workspace_other_root_path): +def test_document_path_completions(tmpdir, workspace_other_root_path) -> None: # Create a dummy module out of the workspace's root_path and try to get # completions for it in another file placed next to it. module_content = """ @@ -562,7 +562,7 @@ def foo(): assert completions[0]["label"] == "foo()" -def test_file_completions(workspace, tmpdir): +def test_file_completions(workspace, tmpdir) -> None: # Create directory and a file to get completions for them. # Note: `tmpdir`` is the root dir of the `workspace` fixture. That's why we use # it here. diff --git a/test/plugins/test_definitions.py b/test/plugins/test_definitions.py index 5da04ff3..7923524b 100644 --- a/test/plugins/test_definitions.py +++ b/test/plugins/test_definitions.py @@ -41,7 +41,7 @@ def my_func(): """ -def test_definitions(config, workspace): +def test_definitions(config, workspace) -> None: # Over 'a' in print a cursor_pos = {"line": 3, "character": 6} @@ -57,7 +57,7 @@ def test_definitions(config, workspace): ) -def test_indirect_definitions(config, workspace): +def test_indirect_definitions(config, workspace) -> None: # Over 'subscripted_before_reference' cursor_pos = {"line": 16, "character": 0} @@ -74,7 +74,7 @@ def test_indirect_definitions(config, workspace): ) -def test_definition_with_multihop_inference_goto(config, workspace): +def test_definition_with_multihop_inference_goto(config, workspace) -> None: # Over 'inception()' cursor_pos = {"line": 26, "character": 0} @@ -91,7 +91,7 @@ def test_definition_with_multihop_inference_goto(config, workspace): ) -def test_numpy_definition(config, workspace): +def test_numpy_definition(config, workspace) -> None: # Over numpy.ones cursor_pos = {"line": 29, "character": 8} @@ -100,7 +100,7 @@ def test_numpy_definition(config, workspace): assert len(defns) > 0, defns -def test_builtin_definition(config, workspace): +def test_builtin_definition(config, workspace) -> None: # Over 'i' in dict cursor_pos = {"line": 8, "character": 24} @@ -124,7 +124,7 @@ def test_builtin_definition(config, workspace): config.update(orig_settings) -def test_assignment(config, workspace): +def test_assignment(config, workspace) -> None: # Over 's' in self.members[id] cursor_pos = {"line": 11, "character": 19} @@ -140,7 +140,7 @@ def test_assignment(config, workspace): ) -def test_document_path_definitions(config, workspace_other_root_path, tmpdir): +def test_document_path_definitions(config, workspace_other_root_path, tmpdir) -> None: # Create a dummy module out of the workspace's root_path and try to get # a definition on it in another file placed next to it. module_content = """ diff --git a/test/plugins/test_flake8_lint.py b/test/plugins/test_flake8_lint.py index bdc180fd..e7b6b001 100644 --- a/test/plugins/test_flake8_lint.py +++ b/test/plugins/test_flake8_lint.py @@ -30,7 +30,7 @@ def temp_document(doc_text, workspace): return name, doc -def test_flake8_unsaved(workspace): +def test_flake8_unsaved(workspace) -> None: doc = Document("", workspace, DOC) diags = flake8_lint.pylsp_lint(workspace, doc) msg = "F841 local variable 'a' is assigned to but never used" @@ -44,7 +44,7 @@ def test_flake8_unsaved(workspace): assert unused_var["tags"] == [lsp.DiagnosticTag.Unnecessary] -def test_flake8_lint(workspace): +def test_flake8_lint(workspace) -> None: name, doc = temp_document(DOC, workspace) try: diags = flake8_lint.pylsp_lint(workspace, doc) @@ -60,7 +60,7 @@ def test_flake8_lint(workspace): os.remove(name) -def test_flake8_respecting_configuration(workspace): +def test_flake8_respecting_configuration(workspace) -> None: docs = [ ("src/__init__.py", ""), ("src/a.py", DOC), @@ -122,7 +122,7 @@ def test_flake8_respecting_configuration(workspace): ] -def test_flake8_config_param(workspace): +def test_flake8_config_param(workspace) -> None: with patch("pylsp.plugins.flake8_lint.Popen") as popen_mock: mock_instance = popen_mock.return_value mock_instance.communicate.return_value = [bytes(), bytes()] @@ -135,7 +135,7 @@ def test_flake8_config_param(workspace): assert "--config={}".format(flake8_conf) in call_args -def test_flake8_executable_param(workspace): +def test_flake8_executable_param(workspace) -> None: with patch("pylsp.plugins.flake8_lint.Popen") as popen_mock: mock_instance = popen_mock.return_value mock_instance.communicate.return_value = [bytes(), bytes()] @@ -168,7 +168,7 @@ def get_flake8_cfg_settings(workspace, config_str): return workspace._config.plugin_settings("flake8") -def test_flake8_multiline(workspace): +def test_flake8_multiline(workspace) -> None: config_str = r"""[flake8] exclude = blah/, @@ -206,7 +206,7 @@ def test_flake8_multiline(workspace): os.unlink(os.path.join(workspace.root_path, "setup.cfg")) -def test_flake8_per_file_ignores(workspace): +def test_flake8_per_file_ignores(workspace) -> None: config_str = r"""[flake8] ignores = F403 per-file-ignores = @@ -236,7 +236,7 @@ def test_flake8_per_file_ignores(workspace): os.unlink(os.path.join(workspace.root_path, "setup.cfg")) -def test_per_file_ignores_alternative_syntax(workspace): +def test_per_file_ignores_alternative_syntax(workspace) -> None: config_str = r"""[flake8] per-file-ignores = **/__init__.py:F401,E402 """ diff --git a/test/plugins/test_folding.py b/test/plugins/test_folding.py index 733f3993..1f0d34c8 100644 --- a/test/plugins/test_folding.py +++ b/test/plugins/test_folding.py @@ -116,7 +116,7 @@ class A(: ) -def test_folding(workspace): +def test_folding(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) ranges = pylsp_folding_range(doc) expected = [ @@ -160,7 +160,7 @@ def test_folding(workspace): assert ranges == expected -def test_folding_syntax_error(workspace): +def test_folding_syntax_error(workspace) -> None: doc = Document(DOC_URI, workspace, SYNTAX_ERR) ranges = pylsp_folding_range(doc) expected = [ diff --git a/test/plugins/test_highlight.py b/test/plugins/test_highlight.py index fcc9809c..eb5485bb 100644 --- a/test/plugins/test_highlight.py +++ b/test/plugins/test_highlight.py @@ -11,7 +11,7 @@ """ -def test_highlight(workspace): +def test_highlight(workspace) -> None: # Over 'a' in a.startswith cursor_pos = {"line": 1, "character": 0} @@ -41,7 +41,7 @@ def test_highlight(workspace): """ -def test_sys_highlight(workspace): +def test_sys_highlight(workspace) -> None: cursor_pos = {"line": 0, "character": 8} doc = Document(DOC_URI, workspace, SYS_DOC) diff --git a/test/plugins/test_hover.py b/test/plugins/test_hover.py index a65f92dc..9674b872 100644 --- a/test/plugins/test_hover.py +++ b/test/plugins/test_hover.py @@ -23,7 +23,7 @@ def main(): """ -def test_numpy_hover(workspace): +def test_numpy_hover(workspace) -> None: # Over the blank line no_hov_position = {"line": 1, "character": 0} # Over 'numpy' in import numpy as np @@ -71,7 +71,7 @@ def test_numpy_hover(workspace): ) -def test_hover(workspace): +def test_hover(workspace) -> None: # Over 'main' in def main(): hov_position = {"line": 2, "character": 6} # Over the blank second line @@ -86,7 +86,7 @@ def test_hover(workspace): assert {"contents": ""} == pylsp_hover(doc._config, doc, no_hov_position) -def test_document_path_hover(workspace_other_root_path, tmpdir): +def test_document_path_hover(workspace_other_root_path, tmpdir) -> None: # Create a dummy module out of the workspace's root_path and try to get # a definition on it in another file placed next to it. module_content = ''' diff --git a/test/plugins/test_jedi_rename.py b/test/plugins/test_jedi_rename.py index 753303d4..349274be 100644 --- a/test/plugins/test_jedi_rename.py +++ b/test/plugins/test_jedi_rename.py @@ -33,7 +33,7 @@ def tmp_workspace(temp_workspace_factory): ) -def test_jedi_rename(tmp_workspace, config): +def test_jedi_rename(tmp_workspace, config) -> None: # rename the `Test1` class position = {"line": 0, "character": 6} DOC_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC_NAME)) diff --git a/test/plugins/test_mccabe_lint.py b/test/plugins/test_mccabe_lint.py index 6c420b12..f4df0c2c 100644 --- a/test/plugins/test_mccabe_lint.py +++ b/test/plugins/test_mccabe_lint.py @@ -14,7 +14,7 @@ \tpass""" -def test_mccabe(config, workspace): +def test_mccabe(config, workspace) -> None: old_settings = config.settings try: config.update({"plugins": {"mccabe": {"threshold": 1}}}) @@ -34,6 +34,6 @@ def test_mccabe(config, workspace): config._settings = old_settings -def test_mccabe_syntax_error(config, workspace): +def test_mccabe_syntax_error(config, workspace) -> None: doc = Document(DOC_URI, workspace, DOC_SYNTAX_ERR) assert mccabe_lint.pylsp_lint(config, workspace, doc) is None diff --git a/test/plugins/test_pycodestyle_lint.py b/test/plugins/test_pycodestyle_lint.py index 5d83648f..eea0b7de 100644 --- a/test/plugins/test_pycodestyle_lint.py +++ b/test/plugins/test_pycodestyle_lint.py @@ -24,7 +24,7 @@ def hello( ): """ -def test_pycodestyle(workspace): +def test_pycodestyle(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) diags = pycodestyle_lint.pylsp_lint(workspace, doc) @@ -64,7 +64,7 @@ def test_pycodestyle(workspace): assert mod_import["range"]["end"] == {"line": 5, "character": 10} -def test_pycodestyle_config(workspace): +def test_pycodestyle_config(workspace) -> None: """Test that we load config files properly. Config files are loaded in the following order: @@ -118,7 +118,7 @@ def test_pycodestyle_config(workspace): @pytest.mark.parametrize("newline", ["\r\n", "\r"]) -def test_line_endings(workspace, newline): +def test_line_endings(workspace, newline) -> None: """ Check that Pycodestyle doesn't generate false positives with line endings other than LF. diff --git a/test/plugins/test_pydocstyle_lint.py b/test/plugins/test_pydocstyle_lint.py index 22f10d02..383aaf1f 100644 --- a/test/plugins/test_pydocstyle_lint.py +++ b/test/plugins/test_pydocstyle_lint.py @@ -19,7 +19,7 @@ def hello(): """ -def test_pydocstyle(config, workspace): +def test_pydocstyle(config, workspace) -> None: doc = Document(DOC_URI, workspace, DOC) diags = pydocstyle_lint.pylsp_lint(config, workspace, doc) @@ -38,21 +38,21 @@ def test_pydocstyle(config, workspace): } -def test_pydocstyle_test_document(config, workspace): +def test_pydocstyle_test_document(config, workspace) -> None: # The default --match argument excludes test_* documents. doc = Document(TEST_DOC_URI, workspace, "") diags = pydocstyle_lint.pylsp_lint(config, workspace, doc) assert not diags -def test_pydocstyle_empty_source(config, workspace): +def test_pydocstyle_empty_source(config, workspace) -> None: doc = Document(DOC_URI, workspace, "") diags = pydocstyle_lint.pylsp_lint(config, workspace, doc) assert diags[0]["message"] == "D100: Missing docstring in public module" assert len(diags) == 1 -def test_pydocstyle_invalid_source(config, workspace): +def test_pydocstyle_invalid_source(config, workspace) -> None: doc = Document(DOC_URI, workspace, "bad syntax") diags = pydocstyle_lint.pylsp_lint(config, workspace, doc) # We're unable to parse the file, so can't get any pydocstyle diagnostics diff --git a/test/plugins/test_pyflakes_lint.py b/test/plugins/test_pyflakes_lint.py index 6ad774c1..8ab36320 100644 --- a/test/plugins/test_pyflakes_lint.py +++ b/test/plugins/test_pyflakes_lint.py @@ -28,7 +28,7 @@ def hello(): """ -def test_pyflakes(workspace): +def test_pyflakes(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) diags = pyflakes_lint.pylsp_lint(workspace, doc) @@ -40,7 +40,7 @@ def test_pyflakes(workspace): assert unused_import["severity"] == lsp.DiagnosticSeverity.Warning -def test_syntax_error_pyflakes(workspace): +def test_syntax_error_pyflakes(workspace) -> None: doc = Document(DOC_URI, workspace, DOC_SYNTAX_ERR) diag = pyflakes_lint.pylsp_lint(workspace, doc)[0] @@ -52,7 +52,7 @@ def test_syntax_error_pyflakes(workspace): assert diag["severity"] == lsp.DiagnosticSeverity.Error -def test_undefined_name_pyflakes(workspace): +def test_undefined_name_pyflakes(workspace) -> None: doc = Document(DOC_URI, workspace, DOC_UNDEFINED_NAME_ERR) diag = pyflakes_lint.pylsp_lint(workspace, doc)[0] @@ -61,7 +61,7 @@ def test_undefined_name_pyflakes(workspace): assert diag["severity"] == lsp.DiagnosticSeverity.Error -def test_unicode_encoding(workspace): +def test_unicode_encoding(workspace) -> None: doc = Document(DOC_URI, workspace, DOC_ENCODING) diags = pyflakes_lint.pylsp_lint(workspace, doc) diff --git a/test/plugins/test_pylint_lint.py b/test/plugins/test_pylint_lint.py index 8ce10eb9..b4d511d0 100644 --- a/test/plugins/test_pylint_lint.py +++ b/test/plugins/test_pylint_lint.py @@ -26,7 +26,7 @@ def hello(): @contextlib.contextmanager -def temp_document(doc_text, workspace): +def temp_document(doc_text, workspace) -> None: try: with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: name = temp_file.name @@ -36,12 +36,12 @@ def temp_document(doc_text, workspace): os.remove(name) -def write_temp_doc(document, contents): +def write_temp_doc(document, contents) -> None: with open(document.path, "w", encoding="utf-8") as temp_file: temp_file.write(contents) -def test_pylint(config, workspace): +def test_pylint(config, workspace) -> None: with temp_document(DOC, workspace) as doc: diags = pylint_lint.pylsp_lint(config, workspace, doc, True) @@ -66,7 +66,7 @@ def test_pylint(config, workspace): assert unused_import["severity"] == lsp.DiagnosticSeverity.Warning -def test_syntax_error_pylint(config, workspace): +def test_syntax_error_pylint(config, workspace) -> None: with temp_document(DOC_SYNTAX_ERR, workspace) as doc: diag = pylint_lint.pylsp_lint(config, workspace, doc, True)[0] @@ -91,7 +91,7 @@ def test_syntax_error_pylint(config, workspace): assert diag["severity"] == lsp.DiagnosticSeverity.Error -def test_lint_free_pylint(config, workspace): +def test_lint_free_pylint(config, workspace) -> None: # Can't use temp_document because it might give us a file that doesn't # match pylint's naming requirements. We should be keeping this file clean # though, so it works for a test of an empty lint. @@ -101,7 +101,7 @@ def test_lint_free_pylint(config, workspace): ) -def test_lint_caching(workspace): +def test_lint_caching(workspace) -> None: # Pylint can only operate on files, not in-memory contents. We cache the # diagnostics after a run so we can continue displaying them until the file # is saved again. @@ -129,7 +129,7 @@ def test_lint_caching(workspace): assert not pylint_lint.PylintLinter.lint(doc, False, flags) -def test_per_file_caching(config, workspace): +def test_per_file_caching(config, workspace) -> None: # Ensure that diagnostics are cached per-file. with temp_document(DOC, workspace) as doc: assert pylint_lint.pylsp_lint(config, workspace, doc, True) diff --git a/test/plugins/test_references.py b/test/plugins/test_references.py index 6990317e..f5121693 100644 --- a/test/plugins/test_references.py +++ b/test/plugins/test_references.py @@ -35,7 +35,7 @@ def tmp_workspace(temp_workspace_factory): ) -def test_references(tmp_workspace): +def test_references(tmp_workspace) -> None: # Over 'Test1' in class Test1(): position = {"line": 0, "character": 8} DOC1_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC1_NAME)) @@ -65,7 +65,7 @@ def test_references(tmp_workspace): assert doc2_usage_ref["range"]["end"] == {"line": 3, "character": 9} -def test_references_builtin(tmp_workspace): +def test_references_builtin(tmp_workspace) -> None: # Over 'UnicodeError': position = {"line": 4, "character": 7} doc2_uri = uris.from_fs_path(os.path.join(str(tmp_workspace.root_path), DOC2_NAME)) diff --git a/test/plugins/test_signature.py b/test/plugins/test_signature.py index 7c285721..4a0a84ef 100644 --- a/test/plugins/test_signature.py +++ b/test/plugins/test_signature.py @@ -42,7 +42,7 @@ def main(param1=None, """ -def test_no_signature(workspace): +def test_no_signature(workspace) -> None: # Over blank line sig_position = {"line": 9, "character": 0} doc = Document(DOC_URI, workspace, DOC) @@ -51,7 +51,7 @@ def test_no_signature(workspace): assert not sigs -def test_signature(workspace): +def test_signature(workspace) -> None: # Over '( ' in main( sig_position = {"line": 10, "character": 5} doc = Document(DOC_URI, workspace, DOC) @@ -70,7 +70,7 @@ def test_signature(workspace): assert sig_info["activeParameter"] == 0 -def test_multi_line_signature(workspace): +def test_multi_line_signature(workspace) -> None: # Over '( ' in main( sig_position = {"line": 17, "character": 5} doc = Document(DOC_URI, workspace, MULTI_LINE_DOC) @@ -100,7 +100,7 @@ def test_multi_line_signature(workspace): (signature.GOOGLE, " test (str): parameter docstring"), ], ) -def test_docstring_params(regex, doc): +def test_docstring_params(regex, doc) -> None: m = regex.match(doc) assert m.group("param") == "test" assert m.group("doc") == "parameter docstring" diff --git a/test/plugins/test_symbols.py b/test/plugins/test_symbols.py index 4dc74bc6..c00ab935 100644 --- a/test/plugins/test_symbols.py +++ b/test/plugins/test_symbols.py @@ -73,13 +73,13 @@ def sym(name): assert sym("main")["location"]["range"]["end"] == {"line": 12, "character": 0} -def test_symbols_all_scopes(config, workspace): +def test_symbols_all_scopes(config, workspace) -> None: doc = Document(DOC_URI, workspace, DOC) symbols = pylsp_document_symbols(config, doc) helper_check_symbols_all_scope(symbols) -def test_symbols_non_existing_file(config, workspace, tmpdir): +def test_symbols_non_existing_file(config, workspace, tmpdir) -> None: path = tmpdir.join("foo.py") # Check pre-condition: file must not exist assert not path.check(exists=1) @@ -92,7 +92,7 @@ def test_symbols_non_existing_file(config, workspace, tmpdir): @pytest.mark.skipif( PY2 or not LINUX or not CI, reason="tested on linux and python 3 only" ) -def test_symbols_all_scopes_with_jedi_environment(workspace): +def test_symbols_all_scopes_with_jedi_environment(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) # Update config extra environment diff --git a/test/plugins/test_yapf_format.py b/test/plugins/test_yapf_format.py index fddd68b4..f69541a4 100644 --- a/test/plugins/test_yapf_format.py +++ b/test/plugins/test_yapf_format.py @@ -27,14 +27,14 @@ """ -def test_format(workspace): +def test_format(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) res = pylsp_format_document(workspace, doc, None) assert apply_text_edits(doc, res) == "A = ['h', 'w', 'a']\n\nB = ['h', 'w']\n" -def test_range_format(workspace): +def test_range_format(workspace) -> None: doc = Document(DOC_URI, workspace, DOC) def_range = { @@ -47,12 +47,12 @@ def test_range_format(workspace): assert apply_text_edits(doc, res) == "A = ['h', 'w', 'a']\n\nB = ['h',\n\n\n'w']\n" -def test_no_change(workspace): +def test_no_change(workspace) -> None: doc = Document(DOC_URI, workspace, GOOD_DOC) assert not pylsp_format_document(workspace, doc, options=None) -def test_config_file(tmpdir, workspace): +def test_config_file(tmpdir, workspace) -> None: # a config file in the same directory as the source file will be used conf = tmpdir.join(".style.yapf") conf.write("[style]\ncolumn_limit = 14") @@ -69,7 +69,7 @@ def test_config_file(tmpdir, workspace): @pytest.mark.parametrize("newline", ["\r\n"]) -def test_line_endings(workspace, newline): +def test_line_endings(workspace, newline) -> None: doc = Document(DOC_URI, workspace, f"import os;import sys{2 * newline}dict(a=1)") res = pylsp_format_document(workspace, doc, options=None) @@ -79,28 +79,28 @@ def test_line_endings(workspace, newline): ) -def test_format_with_tab_size_option(workspace): +def test_format_with_tab_size_option(workspace) -> None: doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC) res = pylsp_format_document(workspace, doc, {"tabSize": "8"}) assert apply_text_edits(doc, res) == FOUR_SPACE_DOC.replace(" ", " ") -def test_format_with_insert_spaces_option(workspace): +def test_format_with_insert_spaces_option(workspace) -> None: doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC) res = pylsp_format_document(workspace, doc, {"insertSpaces": False}) assert apply_text_edits(doc, res) == FOUR_SPACE_DOC.replace(" ", "\t") -def test_format_with_yapf_specific_option(workspace): +def test_format_with_yapf_specific_option(workspace) -> None: doc = Document(DOC_URI, workspace, FOUR_SPACE_DOC) res = pylsp_format_document(workspace, doc, {"USE_TABS": True}) assert apply_text_edits(doc, res) == FOUR_SPACE_DOC.replace(" ", "\t") -def test_format_returns_text_edit_per_line(workspace): +def test_format_returns_text_edit_per_line(workspace) -> None: single_space_indent = """def wow(): log("x") log("hi")""" diff --git a/test/test_configuration.py b/test/test_configuration.py index a6ebaacc..e6b40121 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -20,7 +20,7 @@ @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_set_flake8_using_init_opts(client_server_pair): +def test_set_flake8_using_init_opts(client_server_pair) -> None: client, server = client_server_pair send_initialize_request(client, INITIALIZATION_OPTIONS) for key, value in INITIALIZATION_OPTIONS["pylsp"]["plugins"].items(): @@ -30,7 +30,9 @@ def test_set_flake8_using_init_opts(client_server_pair): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_set_flake8_using_workspace_did_change_configuration(client_server_pair): +def test_set_flake8_using_workspace_did_change_configuration( + client_server_pair, +) -> None: client, server = client_server_pair send_initialize_request(client, None) assert ( diff --git a/test/test_document.py b/test/test_document.py index dd7b7828..f31d446e 100644 --- a/test/test_document.py +++ b/test/test_document.py @@ -5,23 +5,23 @@ from test.fixtures import DOC, DOC_URI -def test_document_props(doc): +def test_document_props(doc) -> None: assert doc.uri == DOC_URI assert doc.source == DOC -def test_document_lines(doc): +def test_document_lines(doc) -> None: assert len(doc.lines) == 4 assert doc.lines[0] == "import sys\n" -def test_document_source_unicode(workspace): +def test_document_source_unicode(workspace) -> None: document_mem = Document(DOC_URI, workspace, "my source") document_disk = Document(DOC_URI, workspace) assert isinstance(document_mem.source, type(document_disk.source)) -def test_offset_at_position(doc): +def test_offset_at_position(doc) -> None: assert doc.offset_at_position({"line": 0, "character": 8}) == 8 assert doc.offset_at_position({"line": 1, "character": 5}) == 16 assert doc.offset_at_position({"line": 2, "character": 0}) == 12 @@ -29,7 +29,7 @@ def test_offset_at_position(doc): assert doc.offset_at_position({"line": 4, "character": 0}) == 51 -def test_word_at_position(doc): +def test_word_at_position(doc) -> None: """Return the position under the cursor (or last in line if past the end)""" # import sys assert doc.word_at_position({"line": 0, "character": 8}) == "sys" @@ -43,7 +43,7 @@ def test_word_at_position(doc): assert doc.word_at_position({"line": 4, "character": 0}) == "" -def test_document_empty_edit(workspace): +def test_document_empty_edit(workspace) -> None: doc = Document("file:///uri", workspace, "") doc.apply_change( { @@ -57,7 +57,7 @@ def test_document_empty_edit(workspace): assert doc.source == "f" -def test_document_line_edit(workspace): +def test_document_line_edit(workspace) -> None: doc = Document("file:///uri", workspace, "itshelloworld") doc.apply_change( { @@ -71,7 +71,7 @@ def test_document_line_edit(workspace): assert doc.source == "itsgoodbyeworld" -def test_document_multiline_edit(workspace): +def test_document_multiline_edit(workspace) -> None: old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document("file:///uri", workspace, "".join(old)) doc.apply_change( @@ -86,7 +86,7 @@ def test_document_multiline_edit(workspace): assert doc.lines == ["def hello(a, b):\n", " print a, b\n"] -def test_document_end_of_file_edit(workspace): +def test_document_end_of_file_edit(workspace) -> None: old = ["print 'a'\n", "print 'b'\n"] doc = Document("file:///uri", workspace, "".join(old)) doc.apply_change( diff --git a/test/test_language_server.py b/test/test_language_server.py index 6d806f93..9b362110 100644 --- a/test/test_language_server.py +++ b/test/test_language_server.py @@ -17,7 +17,7 @@ @pytest.fixture -def client_exited_server(): +def client_exited_server() -> None: """A fixture that sets up a client/server pair that support checking parent process aliveness and assert the server has already exited """ @@ -30,7 +30,7 @@ def client_exited_server(): @flaky(max_runs=10, min_passes=1) @pytest.mark.skipif(sys.platform == "darwin", reason="Too flaky on Mac") -def test_initialize(client_server_pair): +def test_initialize(client_server_pair) -> None: client, _ = client_server_pair response = client._endpoint.request( "initialize", @@ -45,7 +45,7 @@ def test_initialize(client_server_pair): ) def test_exit_with_parent_process_died( client_exited_server, -): +) -> None: # language server should have already exited before responding lsp_server, mock_process = ( client_exited_server.client, @@ -70,7 +70,7 @@ def test_exit_with_parent_process_died( @pytest.mark.skipif(sys.platform.startswith("linux"), reason="Fails on linux") def test_not_exit_without_check_parent_process_flag( client_server_pair, -): +) -> None: client, _ = client_server_pair response = send_initialize_request(client) assert "capabilities" in response @@ -78,7 +78,7 @@ def test_not_exit_without_check_parent_process_flag( @flaky(max_runs=10, min_passes=1) @pytest.mark.skipif(RUNNING_IN_CI, reason="This test is hanging on CI") -def test_missing_message(client_server_pair): +def test_missing_message(client_server_pair) -> None: client, _ = client_server_pair with pytest.raises(JsonRpcMethodNotFound): client._endpoint.request("unknown_method").result( diff --git a/test/test_notebook_document.py b/test/test_notebook_document.py index d1d3ddc5..ca0d477d 100644 --- a/test/test_notebook_document.py +++ b/test/test_notebook_document.py @@ -15,7 +15,7 @@ ) -def wait_for_condition(condition, timeout=CALL_TIMEOUT_IN_SECONDS): +def wait_for_condition(condition, timeout=CALL_TIMEOUT_IN_SECONDS) -> None: """Wait for a condition to be true, or timeout.""" start_time = time.time() while not condition(): @@ -25,7 +25,7 @@ def wait_for_condition(condition, timeout=CALL_TIMEOUT_IN_SECONDS): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_initialize(client_server_pair): +def test_initialize(client_server_pair) -> None: client, server = client_server_pair response = send_initialize_request(client) assert server.workspace is not None @@ -34,7 +34,7 @@ def test_initialize(client_server_pair): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_workspace_did_change_configuration(client_server_pair): +def test_workspace_did_change_configuration(client_server_pair) -> None: """Test that we can update a workspace config w/o error when a notebook is open.""" client, server = client_server_pair send_initialize_request(client) @@ -83,7 +83,7 @@ def test_workspace_did_change_configuration(client_server_pair): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") def test_notebook_document__did_open( client_server_pair, -): +) -> None: client, server = client_server_pair send_initialize_request(client) @@ -185,7 +185,7 @@ def test_notebook_document__did_open( @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") def test_notebook_document__did_change( client_server_pair, -): +) -> None: client, server = client_server_pair send_initialize_request(client) @@ -420,7 +420,7 @@ def test_notebook_document__did_change( @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") def test_notebook__did_close( client_server_pair, -): +) -> None: client, server = client_server_pair send_initialize_request(client) @@ -455,7 +455,7 @@ def test_notebook__did_close( @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_notebook_definition(client_server_pair): +def test_notebook_definition(client_server_pair) -> None: client, server = client_server_pair send_initialize_request(client) @@ -490,7 +490,7 @@ def test_notebook_definition(client_server_pair): @pytest.mark.skipif(IS_WIN, reason="Flaky on Windows") -def test_notebook_completion(client_server_pair): +def test_notebook_completion(client_server_pair) -> None: """ Tests that completions work across cell boundaries for notebook document support """ diff --git a/test/test_text_edit.py b/test/test_text_edit.py index 2b49d242..1d9115bf 100644 --- a/test/test_text_edit.py +++ b/test/test_text_edit.py @@ -7,7 +7,7 @@ DOC_URI = uris.from_fs_path(__file__) -def test_apply_text_edits_insert(pylsp): +def test_apply_text_edits_insert(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "012345678901234567890123456789") test_doc = pylsp.workspace.get_document(DOC_URI) @@ -108,7 +108,7 @@ def test_apply_text_edits_insert(pylsp): ) -def test_apply_text_edits_replace(pylsp): +def test_apply_text_edits_replace(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "012345678901234567890123456789") test_doc = pylsp.workspace.get_document(DOC_URI) @@ -217,7 +217,7 @@ def test_apply_text_edits_replace(pylsp): ) -def test_apply_text_edits_overlap(pylsp): +def test_apply_text_edits_overlap(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "012345678901234567890123456789") test_doc = pylsp.workspace.get_document(DOC_URI) @@ -275,7 +275,7 @@ def test_apply_text_edits_overlap(pylsp): assert did_throw -def test_apply_text_edits_multiline(pylsp): +def test_apply_text_edits_multiline(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "0\n1\n2\n3\n4") test_doc = pylsp.workspace.get_document(DOC_URI) diff --git a/test/test_uris.py b/test/test_uris.py index e418ef56..41c7f54d 100644 --- a/test/test_uris.py +++ b/test/test_uris.py @@ -16,7 +16,7 @@ ("file:/foo/space%20%3Fbar#frag", "/foo/space ?bar"), ], ) -def test_to_fs_path(uri, path): +def test_to_fs_path(uri, path) -> None: assert uris.to_fs_path(uri) == path @@ -29,7 +29,7 @@ def test_to_fs_path(uri, path): ("file:///C:/far/space%20%3Fboo", "c:\\far\\space ?boo"), ], ) -def test_win_to_fs_path(uri, path): +def test_win_to_fs_path(uri, path) -> None: assert uris.to_fs_path(uri) == path @@ -41,7 +41,7 @@ def test_win_to_fs_path(uri, path): ("/foo/space ?bar", "file:///foo/space%20%3Fbar"), ], ) -def test_from_fs_path(path, uri): +def test_from_fs_path(path, uri) -> None: assert uris.from_fs_path(path) == uri @@ -53,7 +53,7 @@ def test_from_fs_path(path, uri): ("C:\\far\\space ?boo", "file:///c:/far/space%20%3Fboo"), ], ) -def test_win_from_fs_path(path, uri): +def test_win_from_fs_path(path, uri) -> None: assert uris.from_fs_path(path) == uri @@ -68,5 +68,5 @@ def test_win_from_fs_path(path, uri): ), ], ) -def test_uri_with(uri, kwargs, new_uri): +def test_uri_with(uri, kwargs, new_uri) -> None: assert uris.uri_with(uri, **kwargs) == new_uri diff --git a/test/test_utils.py b/test/test_utils.py index 6435efb7..07d04e34 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -19,7 +19,7 @@ CALL_TIMEOUT_IN_SECONDS = 30 -def send_notebook_did_open(client, cells: List[str]): +def send_notebook_did_open(client, cells: List[str]) -> None: """ Sends a notebookDocument/didOpen notification with the given python cells. @@ -72,7 +72,7 @@ def send_initialize_request(client, initialization_options: Dict[str, Any] = Non ).result(timeout=CALL_TIMEOUT_IN_SECONDS) -def start(obj): +def start(obj) -> None: obj.start() @@ -85,7 +85,9 @@ class ClientServerPair: check_parent_process: if True, the server_process will check if the parent process is alive. """ - def __init__(self, start_server_in_process=False, check_parent_process=False): + def __init__( + self, start_server_in_process=False, check_parent_process=False + ) -> None: # Client to Server pipe csr, csw = os.pipe() # Server to client pipe @@ -123,7 +125,7 @@ def _get_parallel_kind(self): @flaky(max_runs=6, min_passes=1) -def test_debounce(): +def test_debounce() -> None: interval = 0.1 obj = mock.Mock() @@ -147,7 +149,7 @@ def call_m(): @flaky(max_runs=6, min_passes=1) -def test_debounce_keyed_by(): +def test_debounce_keyed_by() -> None: interval = 0.1 obj = mock.Mock() @@ -180,12 +182,12 @@ def call_m(key): assert len(obj.mock_calls) == 4 -def test_list_to_string(): +def test_list_to_string() -> None: assert _utils.list_to_string("string") == "string" assert _utils.list_to_string(["a", "r", "r", "a", "y"]) == "a,r,r,a,y" -def test_find_parents(tmpdir): +def test_find_parents(tmpdir) -> None: subsubdir = tmpdir.ensure_dir("subdir", "subsubdir") path = subsubdir.ensure("path.py") test_cfg = tmpdir.ensure("test.cfg") @@ -195,14 +197,14 @@ def test_find_parents(tmpdir): ] -def test_merge_dicts(): +def test_merge_dicts() -> None: assert _utils.merge_dicts( {"a": True, "b": {"x": 123, "y": {"hello": "world"}}}, {"a": False, "b": {"y": [], "z": 987}}, ) == {"a": False, "b": {"x": 123, "y": [], "z": 987}} -def test_clip_column(): +def test_clip_column() -> None: assert _utils.clip_column(0, [], 0) == 0 assert _utils.clip_column(2, ["123"], 0) == 2 assert _utils.clip_column(3, ["123"], 0) == 3 @@ -215,7 +217,7 @@ def test_clip_column(): @mock.patch("docstring_to_markdown.convert") -def test_format_docstring_valid_rst_signature(mock_convert): +def test_format_docstring_valid_rst_signature(mock_convert) -> None: """Test that a valid RST docstring includes the function signature.""" docstring = """A function docstring. @@ -244,7 +246,7 @@ def test_format_docstring_valid_rst_signature(mock_convert): @mock.patch("docstring_to_markdown.convert", side_effect=UnknownFormatError) -def test_format_docstring_invalid_rst_signature(_): +def test_format_docstring_invalid_rst_signature(_) -> None: """Test that an invalid RST docstring includes the function signature.""" docstring = """A function docstring. diff --git a/test/test_workspace.py b/test/test_workspace.py index c810bc5b..dabbdf86 100644 --- a/test/test_workspace.py +++ b/test/test_workspace.py @@ -14,32 +14,32 @@ def path_as_uri(path): return pathlib.Path(os.path.abspath(path)).as_uri() -def test_local(pylsp): +def test_local(pylsp) -> None: """Since the workspace points to the test directory""" assert pylsp.workspace.is_local() -def test_put_document(pylsp): +def test_put_document(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "content") assert DOC_URI in pylsp.workspace._docs -def test_put_notebook_document(pylsp): +def test_put_notebook_document(pylsp) -> None: pylsp.workspace.put_notebook_document(DOC_URI, "jupyter-notebook", []) assert DOC_URI in pylsp.workspace._docs -def test_put_cell_document(pylsp): +def test_put_cell_document(pylsp) -> None: pylsp.workspace.put_cell_document(DOC_URI, NOTEBOOK_URI, "python", "content") assert DOC_URI in pylsp.workspace._docs -def test_get_document(pylsp): +def test_get_document(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "TEXT") assert pylsp.workspace.get_document(DOC_URI).source == "TEXT" -def test_get_missing_document(tmpdir, pylsp): +def test_get_missing_document(tmpdir, pylsp) -> None: source = "TEXT" doc_path = tmpdir.join("test_document.py") doc_path.write(source) @@ -47,7 +47,7 @@ def test_get_missing_document(tmpdir, pylsp): assert pylsp.workspace.get_document(doc_uri).source == "TEXT" -def test_rm_document(pylsp): +def test_rm_document(pylsp) -> None: pylsp.workspace.put_document(DOC_URI, "TEXT") assert pylsp.workspace.get_document(DOC_URI).source == "TEXT" pylsp.workspace.rm_document(DOC_URI) @@ -57,7 +57,7 @@ def test_rm_document(pylsp): @pytest.mark.parametrize( "metafiles", [("setup.py",), ("pyproject.toml",), ("setup.py", "pyproject.toml")] ) -def test_non_root_project(pylsp, metafiles): +def test_non_root_project(pylsp, metafiles) -> None: repo_root = os.path.join(pylsp.workspace.root_path, "repo-root") os.mkdir(repo_root) project_root = os.path.join(repo_root, "project-root") @@ -73,7 +73,7 @@ def test_non_root_project(pylsp, metafiles): assert project_root in test_doc.sys_path() -def test_root_project_with_no_setup_py(pylsp): +def test_root_project_with_no_setup_py(pylsp) -> None: """Default to workspace root.""" workspace_root = pylsp.workspace.root_path test_uri = uris.from_fs_path(os.path.join(workspace_root, "hello/test.py")) @@ -82,7 +82,7 @@ def test_root_project_with_no_setup_py(pylsp): assert workspace_root in test_doc.sys_path() -def test_multiple_workspaces_from_initialize(pylsp_w_workspace_folders): +def test_multiple_workspaces_from_initialize(pylsp_w_workspace_folders) -> None: pylsp, workspace_folders = pylsp_w_workspace_folders assert len(pylsp.workspaces) == 2 @@ -113,7 +113,7 @@ def test_multiple_workspaces_from_initialize(pylsp_w_workspace_folders): assert msg2["uri"] in pylsp.workspaces[folders_uris[1]]._docs -def test_multiple_workspaces(tmpdir, pylsp): +def test_multiple_workspaces(tmpdir, pylsp) -> None: workspace1_dir = tmpdir.mkdir("workspace1") workspace2_dir = tmpdir.mkdir("workspace2") file1 = workspace1_dir.join("file1.py") @@ -150,14 +150,14 @@ def test_multiple_workspaces(tmpdir, pylsp): assert workspace1_uri not in pylsp.workspaces -def test_multiple_workspaces_wrong_removed_uri(pylsp, tmpdir): +def test_multiple_workspaces_wrong_removed_uri(pylsp, tmpdir) -> None: workspace = {"uri": str(tmpdir.mkdir("Test123"))} event = {"added": [], "removed": [workspace]} pylsp.m_workspace__did_change_workspace_folders(event) assert workspace["uri"] not in pylsp.workspaces -def test_root_workspace_changed(pylsp, tmpdir): +def test_root_workspace_changed(pylsp, tmpdir) -> None: test_uri = str(tmpdir.mkdir("Test123")) pylsp.root_uri = test_uri pylsp.workspace._root_uri = test_uri @@ -172,7 +172,7 @@ def test_root_workspace_changed(pylsp, tmpdir): assert workspace2["uri"] == pylsp.root_uri -def test_root_workspace_not_changed(pylsp, tmpdir): +def test_root_workspace_not_changed(pylsp, tmpdir) -> None: # removed uri != root_uri test_uri_1 = str(tmpdir.mkdir("Test12")) pylsp.root_uri = test_uri_1 @@ -206,7 +206,7 @@ def test_root_workspace_not_changed(pylsp, tmpdir): assert new_root_uri == pylsp.root_uri -def test_root_workspace_removed(tmpdir, pylsp): +def test_root_workspace_removed(tmpdir, pylsp) -> None: workspace1_dir = tmpdir.mkdir("workspace1") workspace2_dir = tmpdir.mkdir("workspace2") root_uri = pylsp.root_uri @@ -230,7 +230,7 @@ def test_root_workspace_removed(tmpdir, pylsp): @pytest.mark.skipif(os.name == "nt", reason="Fails on Windows") -def test_workspace_loads_pycodestyle_config(pylsp, tmpdir): +def test_workspace_loads_pycodestyle_config(pylsp, tmpdir) -> None: workspace1_dir = tmpdir.mkdir("Test123") pylsp.root_uri = str(workspace1_dir) pylsp.workspace._root_uri = str(workspace1_dir) @@ -268,7 +268,7 @@ def test_workspace_loads_pycodestyle_config(pylsp, tmpdir): assert seetings["plugins"]["pycodestyle"]["maxLineLength"] == 20 -def test_settings_of_added_workspace(pylsp, tmpdir): +def test_settings_of_added_workspace(pylsp, tmpdir) -> None: test_uri = str(tmpdir.mkdir("Test123")) pylsp.root_uri = test_uri pylsp.workspace._root_uri = test_uri @@ -290,7 +290,7 @@ def test_settings_of_added_workspace(pylsp, tmpdir): assert workspace1_jedi_settings == server_settings["pylsp"]["plugins"]["jedi"] -def test_no_progress_without_capability(workspace, consumer): +def test_no_progress_without_capability(workspace, consumer) -> None: workspace._config.capabilities["window"] = {"workDoneProgress": False} with workspace.report_progress("some_title"): @@ -299,7 +299,7 @@ def test_no_progress_without_capability(workspace, consumer): assert len(consumer.call_args_list) == 0 -def test_progress_simple(workspace, consumer): +def test_progress_simple(workspace, consumer) -> None: workspace._config.capabilities["window"] = {"workDoneProgress": True} with workspace.report_progress("some_title"): @@ -332,7 +332,7 @@ def test_progress_simple(workspace, consumer): @pytest.mark.parametrize("exc", [Exception("something"), TimeoutError()]) def test_progress_initialization_fails_but_is_skipped( workspace, consumer, endpoint, exc -): +) -> None: def failing_token_initialization(self, *_args, **_kwargs): raise exc @@ -355,7 +355,7 @@ def failing_token_initialization(self, *_args, **_kwargs): ] -def test_progress_with_percent(workspace, consumer): +def test_progress_with_percent(workspace, consumer) -> None: workspace._config.capabilities["window"] = {"workDoneProgress": True} with workspace.report_progress( @@ -395,7 +395,7 @@ def test_progress_with_percent(workspace, consumer): ] -def test_progress_with_exception(workspace, consumer): +def test_progress_with_exception(workspace, consumer) -> None: workspace._config.capabilities["window"] = {"workDoneProgress": True} class DummyError(Exception): From 4714d3804bdd21b5dd9955d0629468ba9c19d50b Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Thu, 22 Aug 2024 18:15:07 +0200 Subject: [PATCH 21/22] Infer end position for Pylint diagnostics (#547) --- pylsp/plugins/pylint_lint.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/pylsp/plugins/pylint_lint.py b/pylsp/plugins/pylint_lint.py index beffe6f3..722e831b 100644 --- a/pylsp/plugins/pylint_lint.py +++ b/pylsp/plugins/pylint_lint.py @@ -151,7 +151,11 @@ def lint(cls, document, is_saved, flags=""): "line": line, # It's possible that we're linting an empty file. Even an empty # file might fail linting if it isn't named properly. - "character": len(document.lines[line]) if document.lines else 0, + "character": ( + _find_end_of_identifier(document.lines[line], diag["column"]) + if document.lines + else 0 + ), }, } @@ -338,8 +342,9 @@ def _parse_pylint_stdio_result(document, stdout): "start": {"line": line, "character": character}, "end": { "line": line, - # no way to determine the column - "character": len(document.lines[line]) - 1, + "character": _find_end_of_identifier( + document.lines[line], character + ), }, }, "message": msg, @@ -352,3 +357,11 @@ def _parse_pylint_stdio_result(document, stdout): diagnostics.append(diagnostic) return diagnostics + + +def _find_end_of_identifier(string, start): + """Find the end of the identifier starting at the given position.""" + for i in range(len(string), start, -1): + if string[start:i].isidentifier(): + return i + return len(string) - 1 From 3215ea6492cedb34cdf37f4c5f6c4ed6c1550fdf Mon Sep 17 00:00:00 2001 From: Ryan Clary <9618975+mrclary@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:18:31 -0700 Subject: [PATCH 22/22] Allow `extra_paths` to be placed in front of `sys.path` (#527) --- CONFIGURATION.md | 1 + pylsp/config/schema.json | 5 +++++ pylsp/workspace.py | 31 +++++++++++++++++++++---------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index bb07fce9..0609169b 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -21,6 +21,7 @@ This server can be configured using the `workspace/didChangeConfiguration` metho | `pylsp.plugins.flake8.select` | `array` of unique `string` items | List of errors and warnings to enable. | `null` | | `pylsp.plugins.jedi.auto_import_modules` | `array` of `string` items | List of module names for jedi.settings.auto_import_modules. | `["numpy"]` | | `pylsp.plugins.jedi.extra_paths` | `array` of `string` items | Define extra paths for jedi.Script. | `[]` | +| `pylsp.plugins.jedi.prioritize_extra_paths` | `boolean` | Whether to place extra_paths at the beginning (true) or end (false) of `sys.path` | `false` | | `pylsp.plugins.jedi.env_vars` | `object` | Define environment variables for jedi.Script and Jedi.names. | `null` | | `pylsp.plugins.jedi.environment` | `string` | Define environment for jedi.Script and Jedi.names. | `null` | | `pylsp.plugins.jedi_completion.enabled` | `boolean` | Enable or disable the plugin. | `true` | diff --git a/pylsp/config/schema.json b/pylsp/config/schema.json index 2259f1cc..18248384 100644 --- a/pylsp/config/schema.json +++ b/pylsp/config/schema.json @@ -151,6 +151,11 @@ }, "description": "Define extra paths for jedi.Script." }, + "pylsp.plugins.jedi.prioritize_extra_paths": { + "type": "boolean", + "default": false, + "description": "Whether to place extra_paths at the beginning (true) or end (false) of `sys.path`" + }, "pylsp.plugins.jedi.env_vars": { "type": [ "object", diff --git a/pylsp/workspace.py b/pylsp/workspace.py index 139028cc..23e815bb 100644 --- a/pylsp/workspace.py +++ b/pylsp/workspace.py @@ -521,6 +521,7 @@ def jedi_script(self, position=None, use_document_path=False): extra_paths = [] environment_path = None env_vars = None + prioritize_extra_paths = False if self._config: jedi_settings = self._config.plugin_settings( @@ -537,19 +538,19 @@ def jedi_script(self, position=None, use_document_path=False): extra_paths = jedi_settings.get("extra_paths") or [] env_vars = jedi_settings.get("env_vars") + prioritize_extra_paths = jedi_settings.get("prioritize_extra_paths") - # Drop PYTHONPATH from env_vars before creating the environment because that makes - # Jedi throw an error. + # Drop PYTHONPATH from env_vars before creating the environment to + # ensure that Jedi can startup properly without module name collision. if env_vars is None: env_vars = os.environ.copy() env_vars.pop("PYTHONPATH", None) - environment = ( - self.get_enviroment(environment_path, env_vars=env_vars) - if environment_path - else None + environment = self.get_enviroment(environment_path, env_vars=env_vars) + sys_path = self.sys_path( + environment_path, env_vars, prioritize_extra_paths, extra_paths ) - sys_path = self.sys_path(environment_path, env_vars=env_vars) + extra_paths + project_path = self._workspace.root_path # Extend sys_path with document's path if requested @@ -559,7 +560,7 @@ def jedi_script(self, position=None, use_document_path=False): kwargs = { "code": self.source, "path": self.path, - "environment": environment, + "environment": environment if environment_path else None, "project": jedi.Project(path=project_path, sys_path=sys_path), } @@ -584,14 +585,24 @@ def get_enviroment(self, environment_path=None, env_vars=None): return environment - def sys_path(self, environment_path=None, env_vars=None): + def sys_path( + self, + environment_path=None, + env_vars=None, + prioritize_extra_paths=False, + extra_paths=[], + ): # Copy our extra sys path - # TODO: when safe to break API, use env_vars explicitly to pass to create_environment path = list(self._extra_sys_path) environment = self.get_enviroment( environment_path=environment_path, env_vars=env_vars ) path.extend(environment.get_sys_path()) + if prioritize_extra_paths: + path += extra_paths + path + else: + path += path + extra_paths + return path