Skip to content

Commit 0af31dd

Browse files
authored
feat: Use Ruff for tool validation (#5174)
1 parent a6ed82f commit 0af31dd

2 files changed

Lines changed: 77 additions & 13 deletions

File tree

apps/tools/serializers/tool.py

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import os
77
import pickle
88
import re
9+
import subprocess
10+
import sys
911
import tempfile
1012
import zipfile
1113
from functools import reduce
@@ -22,8 +24,6 @@
2224
from django.utils.translation import gettext_lazy as _
2325
from langchain_core.messages import HumanMessage, AIMessage
2426
from langchain_mcp_adapters.client import MultiServerMCPClient
25-
from pylint.lint import Run
26-
from pylint.reporters import JSON2Reporter
2727
from rest_framework import serializers, status
2828

2929
from application.models import Application
@@ -737,17 +737,81 @@ def run(self, instance, is_valid=True):
737737
if is_valid:
738738
self.is_valid(raise_exception=True)
739739
PylintInstance(data=instance).is_valid(raise_exception=True)
740-
code = instance.get('code')
740+
741+
code = instance.get('code') or ''
741742
file_name = get_file_name()
742-
with open(file_name, 'w') as file:
743-
file.write(code)
744-
reporter = JSON2Reporter(output=io.StringIO())
745-
Run([file_name,
746-
"--disable=line-too-long",
747-
'--module-rgx=[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'],
748-
reporter=reporter, exit=False)
749-
os.remove(file_name)
750-
return [to_dict(m, os.path.basename(file_name)) for m in reporter.messages]
743+
if not file_name.endswith('.py'):
744+
file_name = file_name + '.py'
745+
746+
try:
747+
with open(file_name, 'w', encoding='utf-8') as file:
748+
file.write(code)
749+
result = subprocess.run(
750+
[
751+
sys.executable,
752+
'-m',
753+
'ruff',
754+
'check',
755+
file_name,
756+
'--output-format=json',
757+
'--ignore=E501',
758+
'--extend-select=PL',
759+
'--no-cache',
760+
],
761+
stdout=subprocess.PIPE,
762+
stderr=subprocess.PIPE,
763+
text=True,
764+
timeout=10,
765+
)
766+
767+
if result.returncode not in (0, 1):
768+
raise Exception(result.stderr or result.stdout)
769+
770+
if not result.stdout.strip():
771+
return []
772+
773+
messages = json.loads(result.stdout)
774+
base_name = os.path.basename(file_name)
775+
return [
776+
{
777+
'type': 'error' if (
778+
item.get('code') == 'E999'
779+
or str(item.get('code') or '').startswith('E9')
780+
or item.get('code') in ['F821', 'F822', 'F823']
781+
) else 'warning',
782+
'module': '',
783+
'obj': '',
784+
'line': item.get('location', {}).get('row', 1),
785+
786+
# Ruff column 是 1-based,前端 CodeMirror 用 0-based
787+
'column': max(item.get('location', {}).get('column', 1) - 1, 0),
788+
789+
'endLine': item.get('end_location', {}).get(
790+
'row',
791+
item.get('location', {}).get('row', 1)
792+
),
793+
'endColumn': max(
794+
item.get('end_location', {}).get(
795+
'column',
796+
item.get('location', {}).get('column', 1) + 1
797+
) - 1,
798+
0
799+
),
800+
801+
'path': base_name,
802+
'symbol': item.get('code') or '',
803+
'message': (
804+
f"{item.get('code')}: {item.get('message')}"
805+
if item.get('code')
806+
else item.get('message')
807+
),
808+
}
809+
for item in messages
810+
]
811+
812+
finally:
813+
if os.path.exists(file_name):
814+
os.remove(file_name)
751815

752816
class Import(serializers.Serializer):
753817
file = UploadedFileField(required=True, label=_("file"))

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ dependencies = [
6161
"gunicorn==23.0.0",
6262
"python-daemon==3.1.2",
6363
"websockets==15.0.1",
64-
"pylint==3.3.7",
64+
"ruff==0.15.12",
6565
"cohere==5.17.0",
6666
"jsonpath-ng==1.8.0"
6767
]

0 commit comments

Comments
 (0)