Skip to content

Commit 709aedc

Browse files
committed
feat: Allow ruff to be used as a formatter
1 parent cea4996 commit 709aedc

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

src/mkdocstrings_handlers/python/rendering.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from __future__ import annotations
44

55
import enum
6+
import importlib
67
import random
78
import re
89
import string
10+
import subprocess
911
import sys
1012
import warnings
1113
from functools import lru_cache
@@ -83,7 +85,7 @@ def do_format_code(code: str, line_length: int) -> str:
8385
code = code.strip()
8486
if len(code) < line_length:
8587
return code
86-
formatter = _get_black_formatter()
88+
formatter = _get_formatter()
8789
return formatter(code, line_length)
8890

8991

@@ -118,7 +120,7 @@ def _format_signature(name: Markup, signature: str, line_length: int) -> str:
118120
# Black cannot format names with dots, so we replace
119121
# the whole name with a string of equal length
120122
name_length = len(name)
121-
formatter = _get_black_formatter()
123+
formatter = _get_formatter()
122124
formatable = f"def {'x' * name_length}{signature}: pass"
123125
formatted = formatter(formatable, line_length)
124126

@@ -434,12 +436,53 @@ def do_filter_objects(
434436

435437

436438
@lru_cache(maxsize=1)
437-
def _get_black_formatter() -> Callable[[str, int], str]:
439+
def _get_formatter() -> Callable[[str, int], str]:
440+
for formatter_function in [
441+
_get_black_formatter,
442+
_get_ruff_formatter,
443+
]:
444+
if (formatter := formatter_function()) is not None:
445+
return formatter
446+
447+
logger.info("Formatting signatures requires either Black or ruff to be installed.")
448+
return lambda text, _: text
449+
450+
451+
@lru_cache(maxsize=1)
452+
def _get_ruff_formatter() -> Callable[[str, int], str] | None:
453+
if importlib.util.find_spec("ruff") is None:
454+
return None
455+
456+
def formatter(code: str, line_length: int) -> str:
457+
try:
458+
completed_process = subprocess.run( # noqa: S603
459+
[ # noqa: S607
460+
"ruff",
461+
"format",
462+
f'--config "line-length={line_length}"',
463+
"--stdin-filename",
464+
"file.py",
465+
"-",
466+
],
467+
check=True,
468+
capture_output=True,
469+
text=True,
470+
input=code,
471+
)
472+
except subprocess.CalledProcessError:
473+
return code
474+
else:
475+
return completed_process.stdout
476+
477+
return formatter
478+
479+
480+
@lru_cache(maxsize=1)
481+
def _get_black_formatter() -> Callable[[str, int], str] | None:
438482
try:
439483
from black import InvalidInput, Mode, format_str
440484
except ModuleNotFoundError:
441-
logger.info("Formatting signatures requires Black to be installed.")
442-
return lambda text, _: text
485+
return None
443486

444487
def formatter(code: str, line_length: int) -> str:
445488
mode = Mode(line_length=line_length)

tests/test_rendering.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import re
66
from dataclasses import dataclass
7-
from typing import TYPE_CHECKING, Any
7+
from typing import TYPE_CHECKING, Any, Callable
88

99
import pytest
1010
from griffe import ModulesCollection, temporary_visited_module
@@ -22,14 +22,22 @@
2222
"aaaaa(bbbbb, ccccc=1) + ddddd.eeeee[ffff] or {ggggg: hhhhh, iiiii: jjjjj}",
2323
],
2424
)
25-
def test_format_code(code: str) -> None:
25+
@pytest.mark.parametrize(
26+
"formatter",
27+
[
28+
rendering._get_black_formatter(),
29+
rendering._get_ruff_formatter(),
30+
rendering._get_formatter(),
31+
],
32+
)
33+
def test_format_code(code: str, formatter: Callable[[str, int], str]) -> None:
2634
"""Assert code can be Black-formatted.
2735
2836
Parameters:
2937
code: Code to format.
3038
"""
3139
for length in (5, 100):
32-
assert rendering.do_format_code(code, length)
40+
assert formatter(code, length)
3341

3442

3543
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)