From b0a7ac95033047168284a590ed880022194c4b38 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Sun, 14 Jun 2026 06:54:28 -0600 Subject: [PATCH] feat: add formatter module wrapping ruff and clang-format --- formatter.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ test_formatter.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 formatter.py create mode 100644 test_formatter.py diff --git a/formatter.py b/formatter.py new file mode 100644 index 0000000..e4c304b --- /dev/null +++ b/formatter.py @@ -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}") diff --git a/test_formatter.py b/test_formatter.py new file mode 100644 index 0000000..38af339 --- /dev/null +++ b/test_formatter.py @@ -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")