Skip to content

Commit 3329551

Browse files
Optimization: use stdin for ruff to reduce disk access (#25)
* Run ruff with stdin to prevent disk access and speed up linting and formatting * Remove unused configuration for ruff (color)
1 parent 1e8e55b commit 3329551

1 file changed

Lines changed: 81 additions & 102 deletions

File tree

ruff_cgx/utils.py

Lines changed: 81 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
import os
55
import re
66
import subprocess
7-
import tempfile
87
import textwrap
9-
from contextlib import contextmanager
108
from dataclasses import dataclass
11-
from pathlib import Path
129
from typing import List
1310

1411
from collagraph.sfc.compiler import construct_ast
@@ -267,7 +264,7 @@ def run_ruff_format(
267264
source: str, *, use_single_quotes: bool = False, check: bool = False
268265
) -> str:
269266
"""
270-
Format Python source code using ruff.
267+
Format Python source code using ruff via stdin.
271268
272269
Args:
273270
source: The Python source code to format
@@ -277,123 +274,105 @@ def run_ruff_format(
277274
Returns:
278275
Formatted Python source code
279276
"""
280-
ruff_command = [get_ruff_command(), "format"]
277+
should_sort_imports = is_isort_configured()
278+
279+
# Sort imports first if configured
280+
if should_sort_imports:
281+
import_sort_command = [
282+
get_ruff_command(),
283+
"check",
284+
"--select",
285+
"I",
286+
"--fix",
287+
"--stdin-filename",
288+
"source.py",
289+
]
290+
result = subprocess.run(
291+
import_sort_command,
292+
input=source,
293+
capture_output=True,
294+
text=True,
295+
)
296+
# Use the fixed output if available, otherwise use original
297+
if result.returncode == 0 or result.stdout:
298+
source = result.stdout if result.stdout else source
299+
300+
# Build format command
301+
ruff_command = [
302+
get_ruff_command(),
303+
"format",
304+
"--stdin-filename",
305+
"source.py",
306+
]
307+
281308
if check:
282309
ruff_command.append("--check")
283310

284-
should_sort_imports = is_isort_configured()
311+
# Configure quote style and indent width via inline TOML config
312+
if use_single_quotes:
313+
ruff_command.extend(
314+
[
315+
"--config",
316+
"format.quote-style = 'single'",
317+
"--config",
318+
"indent-width = 2",
319+
]
320+
)
321+
322+
# Run ruff format with stdin
323+
result = subprocess.run(
324+
ruff_command,
325+
input=source,
326+
capture_output=True,
327+
text=True,
328+
)
285329

286-
with tempfile.TemporaryDirectory() as directory:
287-
target_file = Path(directory) / "source.py"
288-
target_file.write_text(source, encoding="utf-8")
289-
290-
# Create config if single quotes requested
291-
if use_single_quotes:
292-
config_file = Path(directory) / "ruff.toml"
293-
config_file.write_text(
294-
'indent-width = 2\n[format]\nquote-style = "single"\n',
295-
encoding="utf-8",
296-
)
297-
ruff_command.extend(["--config", str(config_file)])
298-
299-
ruff_command.append(str(target_file))
300-
301-
# Enable color output
302-
env = os.environ.copy()
303-
env["CLICOLOR_FORCE"] = "1"
304-
305-
# Run ruff
306-
if should_sort_imports:
307-
# Sort imports with: ruff check --select I --fix .
308-
result = subprocess.run(
309-
[
310-
get_ruff_command(),
311-
"check",
312-
"--select",
313-
"I",
314-
"--fix",
315-
str(target_file),
316-
],
317-
capture_output=True,
318-
text=True,
319-
env=env,
320-
)
321-
# Then do the formatting
322-
result = subprocess.run(ruff_command, capture_output=True, text=True, env=env)
323-
324-
if result.returncode == 0 or not check:
325-
return target_file.read_text(encoding="utf-8")
326-
else:
327-
# If check mode and would change, return original
328-
return source
330+
if result.returncode == 0 or not check:
331+
# Format succeeded or not in check mode
332+
return result.stdout if result.stdout else source
333+
else:
334+
# If check mode and would change, return original
335+
return source
329336

330337

331338
def run_ruff_check(
332339
source: str, fix: bool = False
333340
) -> tuple[subprocess.CompletedProcess, str | None]:
334341
"""
335-
Run ruff check on Python source code.
342+
Run ruff check on Python source code via stdin.
336343
337344
Args:
338345
source: The Python source code to check
339346
fix: Whether to apply fixes (default: False)
340347
341348
Returns:
342-
Tuple of (CompletedProcess with the ruff result, temp file path,
349+
Tuple of (CompletedProcess with the ruff result,
343350
fixed content if fix=True else None)
344351
"""
345-
with temp_py_file(source) as temp_path:
346-
ruff_command = [
347-
get_ruff_command(),
348-
"check",
349-
"--output-format=json",
350-
"--no-cache",
351-
"--ignore=RUF100", # Ignore unused noqa (we add these for virtual render)
352-
]
353-
354-
if fix:
355-
ruff_command.append("--fix")
356-
357-
ruff_command.append(str(temp_path))
358-
359-
env = os.environ.copy()
360-
env["CLICOLOR_FORCE"] = "1"
361-
362-
result = subprocess.run(
363-
ruff_command, capture_output=True, text=True, env=env, timeout=30
364-
)
365-
366-
# If fix was requested, read back the fixed content
367-
fixed_content = None
368-
if fix:
369-
fixed_content = temp_path.read_text(encoding="utf-8")
370-
371-
return result, fixed_content
372-
373-
374-
@contextmanager
375-
def temp_py_file(content: str):
376-
"""
377-
Create a temporary Python file with the given content.
352+
ruff_command = [
353+
get_ruff_command(),
354+
"check",
355+
"--output-format=json",
356+
"--no-cache",
357+
"--ignore=RUF100", # Ignore unused noqa (we add these for virtual render)
358+
"--stdin-filename",
359+
"source.py",
360+
]
378361

379-
Args:
380-
content: The Python code to write to the file
362+
if fix:
363+
ruff_command.append("--fix")
381364

382-
Yields:
383-
Path to the temporary file
365+
result = subprocess.run(
366+
ruff_command,
367+
input=source,
368+
capture_output=True,
369+
text=True,
370+
timeout=30,
371+
)
384372

385-
Example:
386-
with temp_py_file("print('hello')") as path:
387-
result = subprocess.run(['python', str(path)])
388-
"""
389-
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
390-
f.write(content)
391-
temp_path = Path(f.name)
373+
# If fix was requested, the fixed content is in stdout
374+
fixed_content = None
375+
if fix and result.stdout:
376+
fixed_content = result.stdout
392377

393-
try:
394-
yield temp_path
395-
finally:
396-
try:
397-
temp_path.unlink()
398-
except Exception:
399-
pass
378+
return result, fixed_content

0 commit comments

Comments
 (0)