Skip to content

Commit

Permalink
Add ruff 0.1.0 capabilities.
Browse files Browse the repository at this point in the history
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 <julian.hossbach@gmx.de>
  • Loading branch information
kstrauser and jhossbach committed Oct 20, 2023
1 parent b71fcd3 commit b183eb1
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 8 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ pip install python-lsp-ruff

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

# Usage
### When using ruff before version 0.1.0
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`:

```sh
pip install "ruff<0.1.0" "python-lsp-ruff==1.5.3"
```

## Usage

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

# Configuration
## Configuration

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

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

## Custom severities
### Custom severities

By default, all diagnostics are marked as warning, except for `"E999"` and all error codes starting with `"F"`, which are displayed as errors.
This default can be changed through the `pylsp.plugins.ruff.severities` option, which takes the error code as a key and any of
Expand Down
31 changes: 28 additions & 3 deletions pylsp_ruff/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ def pylsp_lint(workspace: Workspace, document: Document) -> List[Dict]:


def create_diagnostic(check: RuffCheck, settings: PluginSettings) -> Diagnostic:
"""
Create a LSP diagnostic based on the given RuffCheck object.
Parameters
----------
check : RuffCheck
RuffCheck object to convert.
settings : PluginSettings
Current settings.
Returns
-------
Diagnostic
"""
# Adapt range to LSP specification (zero-based)
range = Range(
start=Position(
Expand Down Expand Up @@ -214,6 +228,8 @@ def pylsp_code_actions(
code_actions = []
has_organize_imports = False

settings = load_settings(workspace=workspace, document_path=document.path)

for diagnostic in diagnostics:
code_actions.append(
create_disable_code_action(document=document, diagnostic=diagnostic)
Expand All @@ -222,6 +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 diagnostic.code == "I001":
code_actions.append(
create_organize_imports_code_action(
Expand All @@ -236,7 +256,6 @@ def pylsp_code_actions(
),
)

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

if stderr:
if p.returncode != 0:
log.error(f"Error running ruff: {stderr.decode()}")

return stdout.decode()
Expand Down Expand Up @@ -491,8 +510,10 @@ def build_arguments(
args = []
# Suppress update announcements
args.append("--quiet")
# Suppress exit 1 when violations were found
args.append("--exit-zero")
# Use the json formatting for easier evaluation
args.append("--format=json")
args.append("--output-format=json")
if fix:
args.append("--fix")
else:
Expand All @@ -510,6 +531,9 @@ def build_arguments(
if settings.line_length:
args.append(f"--line-length={settings.line_length}")

if settings.unsafe_fixes:
args.append("--unsafe-fixes")

if settings.exclude:
args.append(f"--exclude={','.join(settings.exclude)}")

Expand Down Expand Up @@ -583,6 +607,7 @@ def load_settings(workspace: Workspace, document_path: str) -> PluginSettings:
return PluginSettings(
enabled=plugin_settings.enabled,
executable=plugin_settings.executable,
unsafe_fixes=plugin_settings.unsafe_fixes,
extend_ignore=plugin_settings.extend_ignore,
extend_select=plugin_settings.extend_select,
format=plugin_settings.format,
Expand Down
1 change: 1 addition & 0 deletions pylsp_ruff/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Edit:
class Fix:
edits: List[Edit]
message: str
applicability: str


@dataclass
Expand Down
1 change: 1 addition & 0 deletions pylsp_ruff/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
class PluginSettings:
enabled: bool = True
executable: str = "ruff"
unsafe_fixes: bool = False

config: Optional[str] = None
line_length: Optional[int] = None
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "README.md"
requires-python = ">=3.7"
license = {text = "MIT"}
dependencies = [
"ruff>=0.0.267,<0.1.0",
"ruff>=0.1.0, <0.2.0",
"python-lsp-server",
"lsprotocol>=2022.0.0a1",
"tomli>=1.1.0; python_version < '3.11'",
Expand Down
29 changes: 29 additions & 0 deletions tests/test_code_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,40 @@ def f():
pass
"""
)
expected_str_safe = dedent(
"""
def f():
a = 2
"""
)
workspace._config.update(
{
"plugins": {
"ruff": {
"unsafeFixes": True,
}
}
}
)
_, doc = temp_document(codeaction_str, workspace)
settings = ruff_lint.load_settings(workspace, doc.path)
fixed_str = ruff_lint.run_ruff_fix(doc, settings)
assert fixed_str == expected_str

workspace._config.update(
{
"plugins": {
"ruff": {
"unsafeFixes": False,
}
}
}
)
_, doc = temp_document(codeaction_str, workspace)
settings = ruff_lint.load_settings(workspace, doc.path)
fixed_str = ruff_lint.run_ruff_fix(doc, settings)
assert fixed_str == expected_str_safe


def test_format_document_default_settings(workspace):
_, doc = temp_document(import_str, workspace)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_ruff_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ def f():
assert call_args == [
"ruff",
"--quiet",
"--format=json",
"--exit-zero",
"--output-format=json",
"--no-fix",
"--force-exclude",
f"--stdin-filename={os.path.join(workspace.root_path, '__init__.py')}",
Expand Down

0 comments on commit b183eb1

Please sign in to comment.