|
1 | 1 | import enum
|
| 2 | +import importlib.util |
2 | 3 | import json
|
3 | 4 | import logging
|
4 | 5 | import re
|
| 6 | +import shutil |
5 | 7 | import sys
|
| 8 | +from functools import lru_cache |
6 | 9 | from pathlib import PurePath
|
7 | 10 | from subprocess import PIPE, Popen
|
8 | 11 | from typing import Dict, Generator, List, Optional
|
@@ -116,7 +119,6 @@ def pylsp_format_document(workspace: Workspace, document: Document) -> Generator
|
116 | 119 | Document to apply ruff on.
|
117 | 120 |
|
118 | 121 | """
|
119 |
| - |
120 | 122 | log.debug(f"textDocument/formatting: {document}")
|
121 | 123 | outcome = yield
|
122 | 124 | result = outcome.get_result()
|
@@ -178,7 +180,6 @@ def pylsp_lint(workspace: Workspace, document: Document) -> List[Dict]:
|
178 | 180 | List of dicts containing the diagnostics.
|
179 | 181 |
|
180 | 182 | """
|
181 |
| - |
182 | 183 | with workspace.report_progress("lint: ruff"):
|
183 | 184 | settings = load_settings(workspace, document.path)
|
184 | 185 | checks = run_ruff_check(document=document, settings=settings)
|
@@ -487,6 +488,37 @@ def run_ruff_format(
|
487 | 488 | )
|
488 | 489 |
|
489 | 490 |
|
| 491 | +@lru_cache |
| 492 | +def find_executable(executable) -> List[str]: |
| 493 | + cmd = None |
| 494 | + # use the explicit executable configuration |
| 495 | + if executable is not None: |
| 496 | + exe_path = shutil.which(executable) |
| 497 | + if exe_path is not None: |
| 498 | + cmd = [exe_path] |
| 499 | + else: |
| 500 | + log.error(f"Configured ruff executable not found: {executable!r}") |
| 501 | + |
| 502 | + # try the python module |
| 503 | + if cmd is None: |
| 504 | + if importlib.util.find_spec("ruff") is not None: |
| 505 | + cmd = [sys.executable, "-m", "ruff"] |
| 506 | + |
| 507 | + # try system's ruff executable |
| 508 | + if cmd is None: |
| 509 | + system_exe = shutil.which("ruff") |
| 510 | + if system_exe is not None: |
| 511 | + cmd = [system_exe] |
| 512 | + |
| 513 | + if cmd is None: |
| 514 | + log.error( |
| 515 | + "No suitable ruff invocation could be found (executable, python module)." |
| 516 | + ) |
| 517 | + cmd = [] |
| 518 | + |
| 519 | + return cmd |
| 520 | + |
| 521 | + |
490 | 522 | def run_ruff(
|
491 | 523 | settings: PluginSettings,
|
492 | 524 | document_path: str,
|
@@ -522,27 +554,14 @@ def run_ruff(
|
522 | 554 |
|
523 | 555 | arguments = subcommand.build_args(document_path, settings, fix, extra_arguments)
|
524 | 556 |
|
525 |
| - p = None |
526 |
| - if executable is not None: |
527 |
| - log.debug(f"Calling {executable} with args: {arguments} on '{document_path}'") |
528 |
| - try: |
529 |
| - cmd = [executable, str(subcommand)] |
530 |
| - cmd.extend(arguments) |
531 |
| - p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) |
532 |
| - except Exception: |
533 |
| - log.error(f"Can't execute ruff with given executable '{executable}'.") |
534 |
| - if p is None: |
535 |
| - log.debug( |
536 |
| - f"Calling ruff via '{sys.executable} -m ruff'" |
537 |
| - f" with args: {arguments} on '{document_path}'" |
538 |
| - ) |
539 |
| - cmd = [sys.executable, "-m", "ruff", str(subcommand)] |
540 |
| - cmd.extend(arguments) |
541 |
| - p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) |
542 |
| - (stdout, stderr) = p.communicate(document_source.encode()) |
| 557 | + cmd = [*find_executable(executable), str(subcommand), *arguments] |
| 558 | + |
| 559 | + log.debug(f"Calling {cmd} on '{document_path}'") |
| 560 | + p = Popen(cmd, stdin=PIPE, stdout=PIPE) |
| 561 | + (stdout, _) = p.communicate(document_source.encode()) |
543 | 562 |
|
544 | 563 | if p.returncode != 0:
|
545 |
| - log.error(f"Error running ruff: {stderr.decode()}") |
| 564 | + log.error(f"Ruff returned {p.returncode} != 0") |
546 | 565 |
|
547 | 566 | return stdout.decode()
|
548 | 567 |
|
|
0 commit comments