| 
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,36 @@ 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 | +            raise RuntimeError(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 | +        raise RuntimeError(  | 
 | 515 | +            "no suitable ruff invocation could be found (executable, python module)"  | 
 | 516 | +        )  | 
 | 517 | + | 
 | 518 | +    return cmd  | 
 | 519 | + | 
 | 520 | + | 
490 | 521 | def run_ruff(  | 
491 | 522 |     settings: PluginSettings,  | 
492 | 523 |     document_path: str,  | 
@@ -522,23 +553,11 @@ def run_ruff(  | 
522 | 553 | 
 
  | 
523 | 554 |     arguments = subcommand.build_args(document_path, settings, fix, extra_arguments)  | 
524 | 555 | 
 
  | 
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)  | 
 | 556 | +    cmd = [*find_executable(executable), str(subcommand)]  | 
 | 557 | +    cmd.extend(arguments)  | 
 | 558 | + | 
 | 559 | +    log.debug(f"Calling {cmd} on '{document_path}'")  | 
 | 560 | +    p = Popen(cmd, stdin=PIPE, stdout=PIPE)  | 
542 | 561 |     (stdout, stderr) = p.communicate(document_source.encode())  | 
543 | 562 | 
 
  | 
544 | 563 |     if p.returncode != 0:  | 
 | 
0 commit comments