Skip to content

Commit b183eb1

Browse files
kstrauserjhossbach
andcommitted
Add ruff 0.1.0 capabilities.
Does the following: - Updates the pyproject.toml file to only support ruff>=0.1.0 - Add the `--unsafe-fixes` option to the LSP config Co-authored-by: Julian Hoßbach <[email protected]>
1 parent b71fcd3 commit b183eb1

File tree

7 files changed

+73
-8
lines changed

7 files changed

+73
-8
lines changed

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ pip install python-lsp-ruff
1717

1818
There also exists an [AUR package](https://aur.archlinux.org/packages/python-lsp-ruff).
1919

20-
# Usage
20+
### When using ruff before version 0.1.0
21+
Ruff version `0.1.0` introduced API changes that are fixed in Python LSP Ruff `v1.6.0`. To continue with `ruff<0.1.0` please use `v1.5.3`, e.g. using `pip`:
22+
23+
```sh
24+
pip install "ruff<0.1.0" "python-lsp-ruff==1.5.3"
25+
```
26+
27+
## Usage
2128

2229
This plugin will disable `pycodestyle`, `pyflakes`, `mccabe` and `pyls_isort` by default, unless they are explicitly enabled in the client configuration.
2330
When enabled, all linting diagnostics will be provided by `ruff`.
@@ -43,7 +50,7 @@ lspconfig.pylsp.setup {
4350
}
4451
```
4552

46-
# Configuration
53+
## Configuration
4754

4855
Configuration options can be passed to the python-language-server. If a `pyproject.toml`
4956
file is present in the project, `python-lsp-ruff` will use these configuration options.
@@ -66,11 +73,12 @@ the valid configuration keys:
6673
- `pylsp.plugins.ruff.select`: List of error codes to enable.
6774
- `pylsp.plugins.ruff.extendSelect`: Same as select, but append to existing error codes.
6875
- `pylsp.plugins.ruff.format`: List of error codes to fix during formatting. The default is `["I"]`, any additional codes are appended to this list.
76+
- `pylsp.plugins.ruff.unsafeFixes`: boolean that enables/disables fixes that are marked "unsafe" by `ruff`. `false` by default.
6977
- `pylsp.plugins.ruff.severities`: Dictionary of custom severity levels for specific codes, see [below](#custom-severities).
7078

7179
For more information on the configuration visit [Ruff's homepage](https://beta.ruff.rs/docs/configuration/).
7280

73-
## Custom severities
81+
### Custom severities
7482

7583
By default, all diagnostics are marked as warning, except for `"E999"` and all error codes starting with `"F"`, which are displayed as errors.
7684
This default can be changed through the `pylsp.plugins.ruff.severities` option, which takes the error code as a key and any of

pylsp_ruff/plugin.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ def pylsp_lint(workspace: Workspace, document: Document) -> List[Dict]:
139139

140140

141141
def create_diagnostic(check: RuffCheck, settings: PluginSettings) -> Diagnostic:
142+
"""
143+
Create a LSP diagnostic based on the given RuffCheck object.
144+
145+
Parameters
146+
----------
147+
check : RuffCheck
148+
RuffCheck object to convert.
149+
settings : PluginSettings
150+
Current settings.
151+
152+
Returns
153+
-------
154+
Diagnostic
155+
"""
142156
# Adapt range to LSP specification (zero-based)
143157
range = Range(
144158
start=Position(
@@ -214,6 +228,8 @@ def pylsp_code_actions(
214228
code_actions = []
215229
has_organize_imports = False
216230

231+
settings = load_settings(workspace=workspace, document_path=document.path)
232+
217233
for diagnostic in diagnostics:
218234
code_actions.append(
219235
create_disable_code_action(document=document, diagnostic=diagnostic)
@@ -222,6 +238,10 @@ def pylsp_code_actions(
222238
if diagnostic.data: # Has fix
223239
fix = converter.structure(diagnostic.data, RuffFix)
224240

241+
# Ignore fix if marked as unsafe and unsafe_fixes are disabled
242+
if fix.applicability != "safe" and not settings.unsafe_fixes:
243+
continue
244+
225245
if diagnostic.code == "I001":
226246
code_actions.append(
227247
create_organize_imports_code_action(
@@ -236,7 +256,6 @@ def pylsp_code_actions(
236256
),
237257
)
238258

239-
settings = load_settings(workspace=workspace, document_path=document.path)
240259
checks = run_ruff_check(document=document, settings=settings)
241260
checks_with_fixes = [c for c in checks if c.fix]
242261
checks_organize_imports = [c for c in checks_with_fixes if c.code == "I001"]
@@ -458,7 +477,7 @@ def run_ruff(
458477
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
459478
(stdout, stderr) = p.communicate(document_source.encode())
460479

461-
if stderr:
480+
if p.returncode != 0:
462481
log.error(f"Error running ruff: {stderr.decode()}")
463482

464483
return stdout.decode()
@@ -491,8 +510,10 @@ def build_arguments(
491510
args = []
492511
# Suppress update announcements
493512
args.append("--quiet")
513+
# Suppress exit 1 when violations were found
514+
args.append("--exit-zero")
494515
# Use the json formatting for easier evaluation
495-
args.append("--format=json")
516+
args.append("--output-format=json")
496517
if fix:
497518
args.append("--fix")
498519
else:
@@ -510,6 +531,9 @@ def build_arguments(
510531
if settings.line_length:
511532
args.append(f"--line-length={settings.line_length}")
512533

534+
if settings.unsafe_fixes:
535+
args.append("--unsafe-fixes")
536+
513537
if settings.exclude:
514538
args.append(f"--exclude={','.join(settings.exclude)}")
515539

@@ -583,6 +607,7 @@ def load_settings(workspace: Workspace, document_path: str) -> PluginSettings:
583607
return PluginSettings(
584608
enabled=plugin_settings.enabled,
585609
executable=plugin_settings.executable,
610+
unsafe_fixes=plugin_settings.unsafe_fixes,
586611
extend_ignore=plugin_settings.extend_ignore,
587612
extend_select=plugin_settings.extend_select,
588613
format=plugin_settings.format,

pylsp_ruff/ruff.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Edit:
1919
class Fix:
2020
edits: List[Edit]
2121
message: str
22+
applicability: str
2223

2324

2425
@dataclass

pylsp_ruff/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class PluginSettings:
1010
enabled: bool = True
1111
executable: str = "ruff"
12+
unsafe_fixes: bool = False
1213

1314
config: Optional[str] = None
1415
line_length: Optional[int] = None

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ readme = "README.md"
1313
requires-python = ">=3.7"
1414
license = {text = "MIT"}
1515
dependencies = [
16-
"ruff>=0.0.267,<0.1.0",
16+
"ruff>=0.1.0, <0.2.0",
1717
"python-lsp-server",
1818
"lsprotocol>=2022.0.0a1",
1919
"tomli>=1.1.0; python_version < '3.11'",

tests/test_code_actions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,40 @@ def f():
115115
pass
116116
"""
117117
)
118+
expected_str_safe = dedent(
119+
"""
120+
def f():
121+
a = 2
122+
"""
123+
)
124+
workspace._config.update(
125+
{
126+
"plugins": {
127+
"ruff": {
128+
"unsafeFixes": True,
129+
}
130+
}
131+
}
132+
)
118133
_, doc = temp_document(codeaction_str, workspace)
119134
settings = ruff_lint.load_settings(workspace, doc.path)
120135
fixed_str = ruff_lint.run_ruff_fix(doc, settings)
121136
assert fixed_str == expected_str
122137

138+
workspace._config.update(
139+
{
140+
"plugins": {
141+
"ruff": {
142+
"unsafeFixes": False,
143+
}
144+
}
145+
}
146+
)
147+
_, doc = temp_document(codeaction_str, workspace)
148+
settings = ruff_lint.load_settings(workspace, doc.path)
149+
fixed_str = ruff_lint.run_ruff_fix(doc, settings)
150+
assert fixed_str == expected_str_safe
151+
123152

124153
def test_format_document_default_settings(workspace):
125154
_, doc = temp_document(import_str, workspace)

tests/test_ruff_lint.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def f():
177177
assert call_args == [
178178
"ruff",
179179
"--quiet",
180-
"--format=json",
180+
"--exit-zero",
181+
"--output-format=json",
181182
"--no-fix",
182183
"--force-exclude",
183184
f"--stdin-filename={os.path.join(workspace.root_path, '__init__.py')}",

0 commit comments

Comments
 (0)