feat: add formatter module wrapping ruff and clang-format
This commit is contained in:
45
formatter.py
Normal file
45
formatter.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import subprocess
|
||||||
|
|
||||||
|
TIMEOUT_SECONDS = 5
|
||||||
|
|
||||||
|
CLANG_FORMAT_STYLE = "{BasedOnStyle: LLVM, IndentWidth: 4, BreakBeforeBraces: Allman}"
|
||||||
|
|
||||||
|
|
||||||
|
class FormatError(Exception):
|
||||||
|
"""代码格式化失败时抛出,message 为可展示给用户的错误信息"""
|
||||||
|
|
||||||
|
|
||||||
|
def _run(cmd: list[str], code: str) -> str:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
input=code,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=TIMEOUT_SECONDS,
|
||||||
|
)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
raise FormatError("格式化超时")
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise FormatError(result.stderr.strip() or "格式化失败")
|
||||||
|
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def format_code(code: str, language: str) -> str:
|
||||||
|
if language in ("python", "turtle"):
|
||||||
|
return _run(["ruff", "format", "-", "--stdin-filename", "main.py"], code)
|
||||||
|
|
||||||
|
if language in ("c", "cpp"):
|
||||||
|
ext = "c" if language == "c" else "cpp"
|
||||||
|
return _run(
|
||||||
|
[
|
||||||
|
"clang-format",
|
||||||
|
f"-style={CLANG_FORMAT_STYLE}",
|
||||||
|
f"-assume-filename=main.{ext}",
|
||||||
|
],
|
||||||
|
code,
|
||||||
|
)
|
||||||
|
|
||||||
|
raise FormatError(f"不支持的语言: {language}")
|
||||||
33
test_formatter.py
Normal file
33
test_formatter.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from formatter import FormatError, format_code
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_python_normalizes_spacing():
|
||||||
|
result = format_code("x=1\n", "python")
|
||||||
|
assert result == "x = 1\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_turtle_uses_python_formatter():
|
||||||
|
result = format_code("t=1\n", "turtle")
|
||||||
|
assert result == "t = 1\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_python_syntax_error_raises():
|
||||||
|
with pytest.raises(FormatError):
|
||||||
|
format_code("def foo(:\n", "python")
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_c_uses_allman_braces():
|
||||||
|
result = format_code("int main(){int x=1;return x;}", "c")
|
||||||
|
assert result.rstrip("\n") == "int main()\n{\n int x = 1;\n return x;\n}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_cpp_uses_allman_braces():
|
||||||
|
result = format_code("class Foo{};", "cpp")
|
||||||
|
assert result.rstrip("\n") == "class Foo\n{\n};"
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_unsupported_language_raises():
|
||||||
|
with pytest.raises(FormatError):
|
||||||
|
format_code("print(1)", "java")
|
||||||
Reference in New Issue
Block a user