6
6
import random
7
7
import re
8
8
import string
9
+ import subprocess
9
10
import sys
10
11
import warnings
11
12
from functools import lru_cache
@@ -71,19 +72,19 @@ def _sort_key_source(item: CollectorItem) -> Any:
71
72
72
73
73
74
def do_format_code (code : str , line_length : int ) -> str :
74
- """Format code using Black .
75
+ """Format code.
75
76
76
77
Parameters:
77
78
code: The code to format.
78
- line_length: The line length to give to Black .
79
+ line_length: The line length.
79
80
80
81
Returns:
81
82
The same code, formatted.
82
83
"""
83
84
code = code .strip ()
84
85
if len (code ) < line_length :
85
86
return code
86
- formatter = _get_black_formatter ()
87
+ formatter = _get_formatter ()
87
88
return formatter (code , line_length )
88
89
89
90
@@ -118,7 +119,7 @@ def _format_signature(name: Markup, signature: str, line_length: int) -> str:
118
119
# Black cannot format names with dots, so we replace
119
120
# the whole name with a string of equal length
120
121
name_length = len (name )
121
- formatter = _get_black_formatter ()
122
+ formatter = _get_formatter ()
122
123
formatable = f"def { 'x' * name_length } { signature } : pass"
123
124
formatted = formatter (formatable , line_length )
124
125
@@ -137,13 +138,13 @@ def do_format_signature(
137
138
annotations : bool | None = None ,
138
139
crossrefs : bool = False , # noqa: ARG001
139
140
) -> str :
140
- """Format a signature using Black .
141
+ """Format a signature.
141
142
142
143
Parameters:
143
144
context: Jinja context, passed automatically.
144
145
callable_path: The path of the callable we render the signature of.
145
146
function: The function we render the signature of.
146
- line_length: The line length to give to Black .
147
+ line_length: The line length.
147
148
annotations: Whether to show type annotations.
148
149
crossrefs: Whether to cross-reference types in the signature.
149
150
@@ -199,13 +200,13 @@ def do_format_attribute(
199
200
* ,
200
201
crossrefs : bool = False , # noqa: ARG001
201
202
) -> str :
202
- """Format an attribute using Black .
203
+ """Format an attribute.
203
204
204
205
Parameters:
205
206
context: Jinja context, passed automatically.
206
207
attribute_path: The path of the callable we render the signature of.
207
208
attribute: The attribute we render the signature of.
208
- line_length: The line length to give to Black .
209
+ line_length: The line length.
209
210
crossrefs: Whether to cross-reference types in the signature.
210
211
211
212
Returns:
@@ -434,12 +435,59 @@ def do_filter_objects(
434
435
435
436
436
437
@lru_cache (maxsize = 1 )
437
- def _get_black_formatter () -> Callable [[str , int ], str ]:
438
+ def _get_formatter () -> Callable [[str , int ], str ]:
439
+ for formatter_function in [
440
+ _get_black_formatter ,
441
+ _get_ruff_formatter ,
442
+ ]:
443
+ if (formatter := formatter_function ()) is not None :
444
+ return formatter
445
+
446
+ logger .info ("Formatting signatures requires either Black or Ruff to be installed." )
447
+ return lambda text , _ : text
448
+
449
+
450
+ def _get_ruff_formatter () -> Callable [[str , int ], str ] | None :
451
+ try :
452
+ from ruff .__main__ import find_ruff_bin
453
+ except ImportError :
454
+ return None
455
+
456
+ try :
457
+ ruff_bin = find_ruff_bin ()
458
+ except FileNotFoundError :
459
+ ruff_bin = "ruff"
460
+
461
+ def formatter (code : str , line_length : int ) -> str :
462
+ try :
463
+ completed_process = subprocess .run ( # noqa: S603
464
+ [
465
+ ruff_bin ,
466
+ "format" ,
467
+ "--config" ,
468
+ f"line-length={ line_length } " ,
469
+ "--stdin-filename" ,
470
+ "file.py" ,
471
+ "-" ,
472
+ ],
473
+ check = True ,
474
+ capture_output = True ,
475
+ text = True ,
476
+ input = code ,
477
+ )
478
+ except subprocess .CalledProcessError :
479
+ return code
480
+ else :
481
+ return completed_process .stdout
482
+
483
+ return formatter
484
+
485
+
486
+ def _get_black_formatter () -> Callable [[str , int ], str ] | None :
438
487
try :
439
488
from black import InvalidInput , Mode , format_str
440
489
except ModuleNotFoundError :
441
- logger .info ("Formatting signatures requires Black to be installed." )
442
- return lambda text , _ : text
490
+ return None
443
491
444
492
def formatter (code : str , line_length : int ) -> str :
445
493
mode = Mode (line_length = line_length )
0 commit comments