From 097e60615b1dd995bc46a76bb9e403b8e0f51476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Ho=C3=9Fbach?= Date: Tue, 21 Nov 2023 09:40:55 +0100 Subject: [PATCH] Update 'unsafe' code actions (#55) - Update unsafe code actions to contain `(unsafe)` at the end of the message - Prevent the `Fix All` code action from applying unsafe codeactions - Update the README to mention this behaviour --- README.md | 6 ++++++ pylsp_ruff/plugin.py | 10 +++++++--- tests/test_code_actions.py | 14 ++++++++------ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 71f02ea..ec7a5bc 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,12 @@ lspconfig.pylsp.setup { } ``` +## Code actions + +`python-lsp-ruff` supports code actions as given by possible fixes by `ruff`. `python-lsp-ruff` also supports [unsafe fixes](https://docs.astral.sh/ruff/linter/#fix-safety). +Fixes considered unsafe by `ruff` are marked `(unsafe)` in the code action. +The `Fix all` code action *only* consideres safe fixes. + ## Configuration Configuration options can be passed to the python-language-server. If a `pyproject.toml` diff --git a/pylsp_ruff/plugin.py b/pylsp_ruff/plugin.py index a36babf..16a9c8f 100644 --- a/pylsp_ruff/plugin.py +++ b/pylsp_ruff/plugin.py @@ -238,9 +238,10 @@ def pylsp_code_actions( if diagnostic.data: # Has fix fix = converter.structure(diagnostic.data, RuffFix) - # Ignore fix if marked as unsafe and unsafe_fixes are disabled - if fix.applicability != "safe" and not settings.unsafe_fixes: - continue + if fix.applicability == "unsafe": + if not settings.unsafe_fixes: + continue + fix.message += " (unsafe)" if diagnostic.code == "I001": code_actions.append( @@ -359,6 +360,9 @@ def create_fix_all_code_action( title = "Ruff: Fix All" kind = CodeActionKind.SourceFixAll + # No unsafe fixes for 'Fix all', see https://github.com/python-lsp/python-lsp-ruff/issues/55 + settings.unsafe_fixes = False + new_text = run_ruff_fix(document=document, settings=settings) range = Range( start=Position(line=0, character=0), diff --git a/tests/test_code_actions.py b/tests/test_code_actions.py index 063b536..acfb160 100644 --- a/tests/test_code_actions.py +++ b/tests/test_code_actions.py @@ -45,7 +45,7 @@ def f(): codeactions = [ "Ruff (F401): Remove unused import: `os`", "Ruff (F401): Disable for this line", - "Ruff (F841): Remove assignment to unused variable `a`", + "Ruff (F841): Remove assignment to unused variable `a` (unsafe)", "Ruff (F841): Disable for this line", "Ruff: Fix All", ] @@ -70,7 +70,9 @@ def temp_document(doc_text, workspace): def test_ruff_code_actions(workspace): _, doc = temp_document(codeaction_str, workspace) - workspace._config.update({"plugins": {"ruff": {"select": ["F"]}}}) + workspace._config.update( + {"plugins": {"ruff": {"select": ["F"], "unsafeFixes": True}}} + ) diags = ruff_lint.pylsp_lint(workspace, doc) range_ = cattrs.unstructure( Range(start=Position(line=0, character=0), end=Position(line=0, character=0)) @@ -79,8 +81,8 @@ def test_ruff_code_actions(workspace): workspace._config, workspace, doc, range=range_, context={"diagnostics": diags} ) actions = converter.structure(actions, List[CodeAction]) - for action in actions: - assert action.title in codeactions + action_titles = list(map(lambda action: action.title, actions)) + assert sorted(codeactions) == sorted(action_titles) def test_import_action(workspace): @@ -104,8 +106,8 @@ def test_import_action(workspace): workspace._config, workspace, doc, range=range_, context={"diagnostics": diags} ) actions = converter.structure(actions, List[CodeAction]) - for action in actions: - assert action.title in codeactions_import + action_titles = list(map(lambda action: action.title, actions)) + assert sorted(codeactions_import) == sorted(action_titles) def test_fix_all(workspace):