diff --git a/formatter.py b/formatter.py index 0e8e6de..5266c73 100644 --- a/formatter.py +++ b/formatter.py @@ -1,5 +1,7 @@ import subprocess +import sqlparse + TIMEOUT_SECONDS = 5 CLANG_FORMAT_STYLE = "{BasedOnStyle: LLVM, IndentWidth: 4, BreakBeforeBraces: Attach}" @@ -27,6 +29,16 @@ def _run(cmd: list[str], code: str) -> str: return result.stdout +def _format_with_sql(code: str) -> str: + # sqlparse 对语法错误宽容,不会抛异常,语法问题留给判题阶段反馈 + # strip_whitespace 会把多条语句压成一行,先按分号拆分再逐条格式化 + statements = sqlparse.split(code) + return "\n\n".join( + sqlparse.format(s, strip_whitespace=True, keyword_case="upper") + for s in statements + ) + + def format_code(code: str, language: str) -> str: if language in ("python", "turtle"): return _run(["ruff", "format", "-", "--stdin-filename", "main.py"], code) @@ -42,4 +54,7 @@ def format_code(code: str, language: str) -> str: code, ) + if language == "sql": + return _format_with_sql(code) + raise FormatError(f"不支持的语言: {language}") diff --git a/pyproject.toml b/pyproject.toml index 9fdb9a9..bebe4b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "aiosqlite==0.21", "uvicorn[standard]>=0.38.0", "ruff>=0.15.17", + "sqlparse>=0.5.5", ] [dependency-groups] diff --git a/requirements.txt b/requirements.txt index 1df971d..56a7714 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ python-dotenv==1.2.1 pytz==2025.2 pyyaml==6.0.3 ruff==0.15.17 +sqlparse==0.5.5 sniffio==1.3.1 starlette==0.50.0 tortoise-orm==0.25.2 diff --git a/test_formatter.py b/test_formatter.py index 38af339..dd95b67 100644 --- a/test_formatter.py +++ b/test_formatter.py @@ -31,3 +31,24 @@ def test_format_cpp_uses_allman_braces(): def test_format_unsupported_language_raises(): with pytest.raises(FormatError): format_code("print(1)", "java") + + +def test_format_sql_uppercases_keywords(): + result = format_code("select * from students where score>60", "sql") + assert result == "SELECT * FROM students WHERE score>60" + + +def test_format_sql_splits_multiple_statements(): + result = format_code( + "delete from students where score<60;insert into students (id) values (9);", + "sql", + ) + assert result == ( + "DELETE FROM students WHERE score<60;\n\n" + "INSERT INTO students (id) VALUES (9);" + ) + + +def test_format_sql_tolerates_syntax_errors(): + result = format_code("select from where", "sql") + assert result == "SELECT FROM WHERE" diff --git a/uv.lock b/uv.lock index 1c9d8f9..298d4b9 100644 --- a/uv.lock +++ b/uv.lock @@ -77,6 +77,7 @@ dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "ruff" }, + { name = "sqlparse" }, { name = "tortoise-orm" }, { name = "uvicorn", extra = ["standard"] }, ] @@ -94,6 +95,7 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.12.3" }, { name = "python-dotenv", specifier = ">=1.1.1" }, { name = "ruff", specifier = ">=0.15.17" }, + { name = "sqlparse", specifier = ">=0.5.5" }, { name = "tortoise-orm", specifier = ">=0.25.1" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.38.0" }, ] @@ -550,6 +552,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + [[package]] name = "starlette" version = "0.50.0"