1087 lines
32 KiB
Python
Executable File
1087 lines
32 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
CodeDetect API文档生成工具
|
||
|
||
自动从源代码生成API文档,支持:
|
||
- REST API端点文档
|
||
- WebSocket事件文档
|
||
- 内部模块API文档
|
||
- 配置参数文档
|
||
- 示例代码生成
|
||
"""
|
||
|
||
import ast
|
||
import os
|
||
import sys
|
||
import json
|
||
import re
|
||
import inspect
|
||
import importlib.util
|
||
from pathlib import Path
|
||
from typing import Dict, List, Any, Optional, Tuple
|
||
from dataclasses import dataclass, asdict
|
||
from datetime import datetime
|
||
import yaml
|
||
import markdown
|
||
|
||
|
||
@dataclass
|
||
class APIEndpoint:
|
||
"""API端点信息"""
|
||
method: str
|
||
path: str
|
||
description: str
|
||
parameters: List[Dict[str, Any]]
|
||
responses: Dict[str, Dict[str, Any]]
|
||
examples: List[Dict[str, Any]]
|
||
authentication: bool
|
||
rate_limit: Optional[str] = None
|
||
|
||
|
||
@dataclass
|
||
class WebSocketEvent:
|
||
"""WebSocket事件信息"""
|
||
event: str
|
||
direction: str # 'client_to_server' or 'server_to_client'
|
||
description: str
|
||
payload: Dict[str, Any]
|
||
examples: List[Dict[str, Any]]
|
||
|
||
|
||
@dataclass
|
||
class ModuleAPI:
|
||
"""模块API信息"""
|
||
module_name: str
|
||
description: str
|
||
classes: List[Dict[str, Any]]
|
||
functions: List[Dict[str, Any]]
|
||
constants: List[Dict[str, Any]]
|
||
examples: List[str]
|
||
|
||
|
||
@dataclass
|
||
class ConfigParameter:
|
||
"""配置参数信息"""
|
||
name: str
|
||
type: str
|
||
default_value: Any
|
||
description: str
|
||
required: bool
|
||
options: Optional[List[str]] = None
|
||
|
||
|
||
class APIDocGenerator:
|
||
"""API文档生成器"""
|
||
|
||
def __init__(self, project_root: str, output_dir: str):
|
||
self.project_root = Path(project_root)
|
||
self.output_dir = Path(output_dir)
|
||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 初始化数据结构
|
||
self.api_endpoints: List[APIEndpoint] = []
|
||
self.websocket_events: List[WebSocketEvent] = []
|
||
self.module_apis: List[ModuleAPI] = []
|
||
self.config_parameters: List[ConfigParameter] = []
|
||
|
||
# 忽略的文件和目录
|
||
self.ignore_patterns = [
|
||
'__pycache__',
|
||
'.git',
|
||
'.pytest_cache',
|
||
'venv',
|
||
'env',
|
||
'node_modules',
|
||
'build',
|
||
'dist',
|
||
'*.pyc'
|
||
]
|
||
|
||
def generate_all_docs(self):
|
||
"""生成所有API文档"""
|
||
print("🚀 开始生成API文档...")
|
||
|
||
# 扫描源代码
|
||
self._scan_source_code()
|
||
|
||
# 解析Flask路由
|
||
self._parse_flask_routes()
|
||
|
||
# 解析WebSocket事件
|
||
self._parse_websocket_events()
|
||
|
||
# 解析配置文件
|
||
self._parse_config_files()
|
||
|
||
# 生成各种文档
|
||
self._generate_rest_api_docs()
|
||
self._generate_websocket_docs()
|
||
self._generate_module_docs()
|
||
self._generate_config_docs()
|
||
self._generate_examples()
|
||
self._generate_index()
|
||
|
||
print("✅ API文档生成完成!")
|
||
|
||
def _scan_source_code(self):
|
||
"""扫描源代码文件"""
|
||
print("📂 扫描源代码...")
|
||
|
||
src_dir = self.project_root / "src"
|
||
if not src_dir.exists():
|
||
print(f"⚠️ 源代码目录不存在: {src_dir}")
|
||
return
|
||
|
||
for py_file in src_dir.rglob("*.py"):
|
||
if any(pattern in str(py_file) for pattern in self.ignore_patterns):
|
||
continue
|
||
|
||
try:
|
||
self._parse_python_file(py_file)
|
||
except Exception as e:
|
||
print(f"⚠️ 解析文件失败 {py_file}: {e}")
|
||
|
||
def _parse_python_file(self, file_path: Path):
|
||
"""解析Python文件"""
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
tree = ast.parse(content)
|
||
|
||
module_name = self._get_module_name(file_path)
|
||
module_api = ModuleAPI(
|
||
module_name=module_name,
|
||
description="",
|
||
classes=[],
|
||
functions=[],
|
||
constants=[],
|
||
examples=[]
|
||
)
|
||
|
||
for node in ast.walk(tree):
|
||
if isinstance(node, ast.ClassDef):
|
||
self._parse_class(node, module_api)
|
||
elif isinstance(node, ast.FunctionDef):
|
||
self._parse_function(node, module_api)
|
||
elif isinstance(node, ast.Assign):
|
||
self._parse_assign(node, module_api)
|
||
|
||
if module_api.classes or module_api.functions:
|
||
self.module_apis.append(module_api)
|
||
|
||
except Exception as e:
|
||
print(f"⚠️ 解析Python文件失败 {file_path}: {e}")
|
||
|
||
def _parse_class(self, node: ast.ClassDef, module_api: ModuleAPI):
|
||
"""解析类定义"""
|
||
class_info = {
|
||
"name": node.name,
|
||
"docstring": ast.get_docstring(node) or "",
|
||
"methods": [],
|
||
"properties": []
|
||
}
|
||
|
||
for item in node.body:
|
||
if isinstance(item, ast.FunctionDef):
|
||
method_info = {
|
||
"name": item.name,
|
||
"docstring": ast.get_docstring(item) or "",
|
||
"parameters": self._get_function_parameters(item),
|
||
"returns": self._get_function_returns(item),
|
||
"decorators": [self._get_decorator_name(d) for d in item.decorator_list]
|
||
}
|
||
class_info["methods"].append(method_info)
|
||
|
||
module_api.classes.append(class_info)
|
||
|
||
def _parse_function(self, node: ast.FunctionDef, module_api: ModuleAPI):
|
||
"""解析函数定义"""
|
||
if not hasattr(node, 'parent_class'): # 不在类中的函数
|
||
function_info = {
|
||
"name": node.name,
|
||
"docstring": ast.get_docstring(node) or "",
|
||
"parameters": self._get_function_parameters(node),
|
||
"returns": self._get_function_returns(node),
|
||
"decorators": [self._get_decorator_name(d) for d in node.decorator_list]
|
||
}
|
||
module_api.functions.append(function_info)
|
||
|
||
def _parse_assign(self, node: ast.Assign, module_api: ModuleAPI):
|
||
"""解析赋值语句"""
|
||
for target in node.targets:
|
||
if isinstance(target, ast.Name):
|
||
if target.id.isupper(): # 常量
|
||
constant_info = {
|
||
"name": target.id,
|
||
"value": self._get_constant_value(node.value),
|
||
"docstring": "" # 可以从注释中提取
|
||
}
|
||
module_api.constants.append(constant_info)
|
||
|
||
def _get_module_name(self, file_path: Path) -> str:
|
||
"""获取模块名"""
|
||
relative_path = file_path.relative_to(self.project_root / "src")
|
||
module_path = str(relative_path.with_suffix('')).replace(os.sep, '.')
|
||
return module_path
|
||
|
||
def _get_function_parameters(self, node: ast.FunctionDef) -> List[Dict[str, Any]]:
|
||
"""获取函数参数"""
|
||
parameters = []
|
||
|
||
for arg in node.args.args:
|
||
param_info = {
|
||
"name": arg.arg,
|
||
"type": "",
|
||
"default": None,
|
||
"docstring": ""
|
||
}
|
||
|
||
# 尝试从类型注解获取类型
|
||
if arg.annotation:
|
||
param_info["type"] = self._get_type_string(arg.annotation)
|
||
|
||
parameters.append(param_info)
|
||
|
||
return parameters
|
||
|
||
def _get_function_returns(self, node: ast.FunctionDef) -> str:
|
||
"""获取函数返回类型"""
|
||
if node.returns:
|
||
return self._get_type_string(node.returns)
|
||
return ""
|
||
|
||
def _get_type_string(self, node: ast.AST) -> str:
|
||
"""获取类型字符串"""
|
||
if isinstance(node, ast.Name):
|
||
return node.id
|
||
elif isinstance(node, ast.Attribute):
|
||
return f"{node.value.id}.{node.attr}"
|
||
elif isinstance(node, ast.Subscript):
|
||
value = self._get_type_string(node.value)
|
||
slice_value = self._get_type_string(node.slice)
|
||
return f"{value}[{slice_value}]"
|
||
else:
|
||
return ""
|
||
|
||
def _get_decorator_name(self, node: ast.AST) -> str:
|
||
"""获取装饰器名称"""
|
||
if isinstance(node, ast.Name):
|
||
return node.id
|
||
elif isinstance(node, ast.Attribute):
|
||
return f"{node.value.id}.{node.attr}"
|
||
return ""
|
||
|
||
def _get_constant_value(self, node: ast.AST) -> Any:
|
||
"""获取常量值"""
|
||
if isinstance(node, ast.Constant):
|
||
return node.value
|
||
elif isinstance(node, ast.List):
|
||
return [self._get_constant_value(elt) for elt in node.elts]
|
||
elif isinstance(node, ast.Dict):
|
||
return {
|
||
self._get_constant_value(k): self._get_constant_value(v)
|
||
for k, v in zip(node.keys, node.values)
|
||
}
|
||
else:
|
||
return str(node)
|
||
|
||
def _parse_flask_routes(self):
|
||
"""解析Flask路由"""
|
||
print("🌐 解析Flask路由...")
|
||
|
||
api_files = [
|
||
self.project_root / "src" / "ui" / "api.py",
|
||
self.project_root / "src" / "api" / "routes.py"
|
||
]
|
||
|
||
for file_path in api_files:
|
||
if not file_path.exists():
|
||
continue
|
||
|
||
try:
|
||
self._parse_flask_file(file_path)
|
||
except Exception as e:
|
||
print(f"⚠️ 解析Flask文件失败 {file_path}: {e}")
|
||
|
||
def _parse_flask_file(self, file_path: Path):
|
||
"""解析Flask文件"""
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
# 使用正则表达式提取路由信息
|
||
route_pattern = r'@(\w+)\.route\([\'"]([^\'\"]+)[\'"](?:,\s*methods=\[([^\]]+)\])?\)'
|
||
|
||
for match in re.finditer(route_pattern, content):
|
||
blueprint = match.group(1)
|
||
path = match.group(2)
|
||
methods = match.group(3)
|
||
|
||
if methods:
|
||
method_list = [m.strip().strip('\'"') for m in methods.split(',')]
|
||
else:
|
||
method_list = ['GET']
|
||
|
||
for method in method_list:
|
||
# 提取函数文档字符串
|
||
func_start = match.end()
|
||
func_pattern = r'def\s+(\w+)\s*\([^)]*\):\s*"""([^"]*)"""'
|
||
func_match = re.search(func_pattern, content[func_start:])
|
||
|
||
description = ""
|
||
if func_match:
|
||
description = func_match.group(2).strip()
|
||
|
||
endpoint = APIEndpoint(
|
||
method=method,
|
||
path=path,
|
||
description=description,
|
||
parameters=[],
|
||
responses={},
|
||
examples=[],
|
||
authentication=True
|
||
)
|
||
|
||
self.api_endpoints.append(endpoint)
|
||
|
||
def _parse_websocket_events(self):
|
||
"""解析WebSocket事件"""
|
||
print("🔌 解析WebSocket事件...")
|
||
|
||
# 从现有文档和代码中提取WebSocket事件
|
||
events = [
|
||
WebSocketEvent(
|
||
event="file_upload",
|
||
direction="client_to_server",
|
||
description="客户端上传文件",
|
||
payload={"file_id": "string", "filename": "string", "size": "number"},
|
||
examples=[{
|
||
"event": "file_upload",
|
||
"data": {"file_id": "file_123", "filename": "test.c", "size": 1024}
|
||
}]
|
||
),
|
||
WebSocketEvent(
|
||
event="verification_progress",
|
||
direction="server_to_client",
|
||
description="验证进度更新",
|
||
payload={"job_id": "string", "progress": "number", "status": "string"},
|
||
examples=[{
|
||
"event": "verification_progress",
|
||
"data": {"job_id": "job_456", "progress": 75, "status": "running"}
|
||
}]
|
||
),
|
||
WebSocketEvent(
|
||
event="verification_result",
|
||
direction="server_to_client",
|
||
description="验证结果通知",
|
||
payload={"job_id": "string", "result": "object", "success": "boolean"},
|
||
examples=[{
|
||
"event": "verification_result",
|
||
"data": {"job_id": "job_456", "result": {...}, "success": true}
|
||
}]
|
||
)
|
||
]
|
||
|
||
self.websocket_events.extend(events)
|
||
|
||
def _parse_config_files(self):
|
||
"""解析配置文件"""
|
||
print("⚙️ 解析配置文件...")
|
||
|
||
config_files = [
|
||
self.project_root / "config" / "default.yaml",
|
||
self.project_root / "config" / "development.yaml",
|
||
self.project_root / "config" / "production.yaml"
|
||
]
|
||
|
||
for config_file in config_files:
|
||
if not config_file.exists():
|
||
continue
|
||
|
||
try:
|
||
with open(config_file, 'r', encoding='utf-8') as f:
|
||
config_data = yaml.safe_load(f)
|
||
|
||
self._parse_config_data(config_data, config_file.name)
|
||
except Exception as e:
|
||
print(f"⚠️ 解析配置文件失败 {config_file}: {e}")
|
||
|
||
def _parse_config_data(self, config_data: Dict[str, Any], filename: str):
|
||
"""解析配置数据"""
|
||
def traverse_config(data: Dict[str, Any], prefix: str = ""):
|
||
for key, value in data.items():
|
||
full_key = f"{prefix}.{key}" if prefix else key
|
||
|
||
if isinstance(value, dict):
|
||
traverse_config(value, full_key)
|
||
else:
|
||
param = ConfigParameter(
|
||
name=full_key,
|
||
type=type(value).__name__,
|
||
default_value=value,
|
||
description=f"配置参数 {full_key} 来自 {filename}",
|
||
required=False
|
||
)
|
||
self.config_parameters.append(param)
|
||
|
||
if isinstance(config_data, dict):
|
||
traverse_config(config_data)
|
||
|
||
def _generate_rest_api_docs(self):
|
||
"""生成REST API文档"""
|
||
print("📄 生成REST API文档...")
|
||
|
||
md_content = """# REST API 文档
|
||
|
||
## 概述
|
||
|
||
CodeDetect提供RESTful API用于代码验证、规范生成和结果查询。
|
||
|
||
## 基础信息
|
||
|
||
- **基础URL**: `http://localhost:8080/api`
|
||
- **认证方式**: Bearer Token
|
||
- **内容类型**: `application/json`
|
||
- **字符编码**: UTF-8
|
||
|
||
## 通用响应格式
|
||
|
||
所有API响应都遵循以下格式:
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {},
|
||
"message": "操作成功",
|
||
"timestamp": "2024-01-01T00:00:00Z"
|
||
}
|
||
```
|
||
|
||
错误响应格式:
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"error": {
|
||
"code": "ERROR_CODE",
|
||
"message": "错误描述",
|
||
"details": {}
|
||
},
|
||
"timestamp": "2024-01-01T00:00:00Z"
|
||
}
|
||
```
|
||
|
||
## API端点列表
|
||
|
||
"""
|
||
|
||
# 按方法分组
|
||
endpoints_by_method = {}
|
||
for endpoint in self.api_endpoints:
|
||
if endpoint.method not in endpoints_by_method:
|
||
endpoints_by_method[endpoint.method] = []
|
||
endpoints_by_method[endpoint.method].append(endpoint)
|
||
|
||
for method in ['GET', 'POST', 'PUT', 'DELETE']:
|
||
if method in endpoints_by_method:
|
||
md_content += f"### {method} 端点\n\n"
|
||
|
||
for endpoint in endpoints_by_method[method]:
|
||
md_content += f"#### {method} {endpoint.path}\n\n"
|
||
|
||
if endpoint.description:
|
||
md_content += f"**描述**: {endpoint.description}\n\n"
|
||
|
||
md_content += f"**认证**: {'是' if endpoint.authentication else '否'}\n\n"
|
||
|
||
if endpoint.parameters:
|
||
md_content += "**参数**:\n\n"
|
||
for param in endpoint.parameters:
|
||
md_content += f"- `{param['name']}` ({param['type']}): {param.get('description', '')}\n"
|
||
md_content += "\n"
|
||
|
||
if endpoint.examples:
|
||
md_content += "**示例**:\n\n"
|
||
for example in endpoint.examples:
|
||
md_content += "```json\n"
|
||
md_content += json.dumps(example, indent=2, ensure_ascii=False)
|
||
md_content += "\n```\n\n"
|
||
|
||
md_content += "---\n\n"
|
||
|
||
# 保存文档
|
||
with open(self.output_dir / "rest-api.md", 'w', encoding='utf-8') as f:
|
||
f.write(md_content)
|
||
|
||
def _generate_websocket_docs(self):
|
||
"""生成WebSocket文档"""
|
||
print("🔌 生成WebSocket文档...")
|
||
|
||
md_content = """# WebSocket API 文档
|
||
|
||
## 概述
|
||
|
||
CodeDetect使用WebSocket提供实时通信,用于进度更新、结果通知等功能。
|
||
|
||
## 连接信息
|
||
|
||
- **WebSocket URL**: `ws://localhost:8080/ws`
|
||
- **协议**: WebSocket
|
||
- **消息格式**: JSON
|
||
|
||
## 消息格式
|
||
|
||
所有WebSocket消息都遵循以下格式:
|
||
|
||
```json
|
||
{
|
||
"event": "事件名称",
|
||
"data": {},
|
||
"timestamp": "2024-01-01T00:00:00Z"
|
||
}
|
||
```
|
||
|
||
## 事件列表
|
||
|
||
"""
|
||
|
||
# 按方向分组
|
||
events_by_direction = {}
|
||
for event in self.websocket_events:
|
||
if event.direction not in events_by_direction:
|
||
events_by_direction[event.direction] = []
|
||
events_by_direction[event.direction].append(event)
|
||
|
||
for direction in ['client_to_server', 'server_to_client']:
|
||
direction_name = "客户端 → 服务器" if direction == 'client_to_server' else "服务器 → 客户端"
|
||
md_content += f"### {direction_name}\n\n"
|
||
|
||
if direction in events_by_direction:
|
||
for event in events_by_direction[direction]:
|
||
md_content += f"#### {event.event}\n\n"
|
||
md_content += f"**描述**: {event.description}\n\n"
|
||
|
||
if event.payload:
|
||
md_content += "**数据结构**:\n\n"
|
||
md_content += "```json\n"
|
||
md_content += json.dumps(event.payload, indent=2, ensure_ascii=False)
|
||
md_content += "\n```\n\n"
|
||
|
||
if event.examples:
|
||
md_content += "**示例**:\n\n"
|
||
for example in event.examples:
|
||
md_content += "```json\n"
|
||
md_content += json.dumps(example, indent=2, ensure_ascii=False)
|
||
md_content += "\n```\n\n"
|
||
|
||
md_content += "---\n\n"
|
||
|
||
# 保存文档
|
||
with open(self.output_dir / "websocket-api.md", 'w', encoding='utf-8') as f:
|
||
f.write(md_content)
|
||
|
||
def _generate_module_docs(self):
|
||
"""生成模块文档"""
|
||
print("📦 生成模块文档...")
|
||
|
||
md_content = """# 内部模块 API 文档
|
||
|
||
## 概述
|
||
|
||
本文档描述了CodeDetect内部模块的API,供开发者和贡献者使用。
|
||
|
||
## 模块列表
|
||
|
||
"""
|
||
|
||
for module in self.module_apis:
|
||
md_content += f"### {module.module_name}\n\n"
|
||
|
||
if module.description:
|
||
md_content += f"**描述**: {module.description}\n\n"
|
||
|
||
if module.classes:
|
||
md_content += "#### 类\n\n"
|
||
for class_info in module.classes:
|
||
md_content += f"##### {class_info['name']}\n\n"
|
||
|
||
if class_info['docstring']:
|
||
md_content += f"**说明**: {class_info['docstring']}\n\n"
|
||
|
||
if class_info['methods']:
|
||
md_content += "**方法**:\n\n"
|
||
for method in class_info['methods']:
|
||
md_content += f"- `{method['name']}({', '.join(p['name'] for p in method['parameters'])})`\n"
|
||
if method['docstring']:
|
||
md_content += f" - {method['docstring']}\n"
|
||
md_content += "\n"
|
||
|
||
if module.functions:
|
||
md_content += "#### 函数\n\n"
|
||
for function in module.functions:
|
||
md_content += f"##### {function['name']}()\n\n"
|
||
|
||
if function['docstring']:
|
||
md_content += f"**说明**: {function['docstring']}\n\n"
|
||
|
||
if function['parameters']:
|
||
md_content += "**参数**:\n\n"
|
||
for param in function['parameters']:
|
||
md_content += f"- `{param['name']}`: {param['type']}\n"
|
||
md_content += "\n"
|
||
|
||
if function['returns']:
|
||
md_content += f"**返回值**: {function['returns']}\n\n"
|
||
|
||
md_content += "---\n\n"
|
||
|
||
# 保存文档
|
||
with open(self.output_dir / "internal-api.md", 'w', encoding='utf-8') as f:
|
||
f.write(md_content)
|
||
|
||
def _generate_config_docs(self):
|
||
"""生成配置文档"""
|
||
print("⚙️ 生成配置文档...")
|
||
|
||
md_content = """# 配置参数文档
|
||
|
||
## 概述
|
||
|
||
本文档描述了CodeDetect的所有配置参数及其说明。
|
||
|
||
## 配置文件位置
|
||
|
||
- 默认配置: `config/default.yaml`
|
||
- 开发环境: `config/development.yaml`
|
||
- 生产环境: `config/production.yaml`
|
||
|
||
## 配置参数列表
|
||
|
||
"""
|
||
|
||
# 按类型分组
|
||
params_by_type = {}
|
||
for param in self.config_parameters:
|
||
if param.type not in params_by_type:
|
||
params_by_type[param.type] = []
|
||
params_by_type[param.type].append(param)
|
||
|
||
for param_type in sorted(params_by_type.keys()):
|
||
md_content += f"### {param_type} 类型\n\n"
|
||
|
||
for param in params_by_type[param_type]:
|
||
md_content += f"#### {param.name}\n\n"
|
||
md_content += f"**默认值**: `{param.default_value}`\n\n"
|
||
md_content += f"**是否必需**: {'是' if param.required else '否'}\n\n"
|
||
md_content += f"**说明**: {param.description}\n\n"
|
||
|
||
if param.options:
|
||
md_content += "**可选值**:\n\n"
|
||
for option in param.options:
|
||
md_content += f"- `{option}`\n"
|
||
md_content += "\n"
|
||
|
||
md_content += "---\n\n"
|
||
|
||
# 保存文档
|
||
with open(self.output_dir / "configuration.md", 'w', encoding='utf-8') as f:
|
||
f.write(md_content)
|
||
|
||
def _generate_examples(self):
|
||
"""生成示例代码"""
|
||
print("💻 生成示例代码...")
|
||
|
||
# 创建示例目录
|
||
examples_dir = self.output_dir / "examples"
|
||
examples_dir.mkdir(exist_ok=True)
|
||
|
||
# 生成各种示例
|
||
examples = {
|
||
"basic_usage.py": """#!/usr/bin/env python3
|
||
"""
|
||
\"\"\"
|
||
CodeDetect基础使用示例
|
||
|
||
演示如何使用CodeDetect进行基本的代码验证。
|
||
\"\"\"
|
||
|
||
import asyncio
|
||
import json
|
||
from pathlib import Path
|
||
|
||
# 导入CodeDetect模块
|
||
from src.verify.cbmc_runner import CBMCRunner
|
||
from src.mutate.engine import MutationEngine
|
||
from src.parse.code_parser import CodeParser
|
||
|
||
|
||
async def basic_verification_example():
|
||
\"\"\"基础验证示例\"\"\"
|
||
print("=== 基础验证示例 ===")
|
||
|
||
# 示例代码
|
||
code = \"\"\"
|
||
int add(int a, int b) {
|
||
return a + b;
|
||
}
|
||
\"\"\"
|
||
|
||
# 解析代码
|
||
parser = CodeParser()
|
||
with open("temp_example.c", "w") as f:
|
||
f.write(code)
|
||
|
||
try:
|
||
# 解析函数信息
|
||
parse_result = parser.parse_file("temp_example.c")
|
||
print(f"找到 {len(parse_result.functions)} 个函数")
|
||
|
||
for func in parse_result.functions:
|
||
print(f"- {func.name}: {func.return_type}")
|
||
print(f" 参数: {[p.name + ': ' + p.type for p in func.parameters]}")
|
||
|
||
# 生成验证规范
|
||
specification = \"\"\"
|
||
void add_test() {
|
||
int a = __CPROVER_nondet_int();
|
||
int b = __CPROVER_nondet_int();
|
||
|
||
// 约束输入范围
|
||
__CPROVER_assume(a >= -1000 && a <= 1000);
|
||
__CPROVER_assume(b >= -1000 && b <= 1000);
|
||
|
||
int result = add(a, b);
|
||
|
||
// 验证结果
|
||
__CPROVER_assert(result == a + b, "addition_correct");
|
||
}
|
||
\"\"\"
|
||
|
||
# 运行验证
|
||
runner = CBMCRunner()
|
||
result = await runner.run_verification(
|
||
function_metadata={"name": "add"},
|
||
source_file="temp_example.c",
|
||
specification=specification
|
||
)
|
||
|
||
print(f"验证结果: {result.status}")
|
||
print(f"执行时间: {result.execution_time}s")
|
||
|
||
finally:
|
||
# 清理临时文件
|
||
if Path("temp_example.c").exists():
|
||
Path("temp_example.c").unlink()
|
||
|
||
|
||
async def mutation_example():
|
||
\"\"\"突变生成示例\"\"\"
|
||
print("\\n=== 突变生成示例 ===")
|
||
|
||
# 基础规范
|
||
base_spec = \"\"\"
|
||
void add_test() {
|
||
int a = __CPROVER_nondet_int();
|
||
int b = __CPROVER_nondet_int();
|
||
int result = add(a, b);
|
||
__CPROVER_assert(result == a + b, "addition_correct");
|
||
}
|
||
\"\"\"
|
||
|
||
# 函数元数据
|
||
function_metadata = [{
|
||
"name": "add",
|
||
"return_type": "int",
|
||
"parameters": [
|
||
{"name": "a", "type": "int"},
|
||
{"name": "b", "type": "int"}
|
||
],
|
||
"complexity_score": 0.3
|
||
}]
|
||
|
||
# 生成突变
|
||
engine = MutationEngine()
|
||
mutations = engine.generate_mutations(
|
||
base_spec,
|
||
function_metadata,
|
||
max_mutations=3
|
||
)
|
||
|
||
print(f"生成了 {len(mutations)} 个突变:")
|
||
for i, mutation in enumerate(mutations):
|
||
print(f"\\n突变 {i+1}:")
|
||
print(f"类型: {mutation.mutation_type}")
|
||
print(f"置信度: {mutation.confidence:.2f}")
|
||
print(f"规范:\\n{mutation.specification}")
|
||
|
||
|
||
async def main():
|
||
\"\"\"主函数\"\"\"
|
||
try:
|
||
await basic_verification_example()
|
||
await mutation_example()
|
||
except Exception as e:
|
||
print(f"示例执行失败: {e}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|
||
""",
|
||
|
||
"freertos_example.py": """#!/usr/bin/env python3
|
||
"""
|
||
\"\"\"
|
||
FreeRTOS验证示例
|
||
|
||
演示如何使用CodeDetect验证FreeRTOS代码。
|
||
\"\"\"
|
||
|
||
import asyncio
|
||
from pathlib import Path
|
||
|
||
from src.verify.cbmc_runner import CBMCRunner
|
||
from src.mutate.engine import MutationEngine
|
||
|
||
|
||
async def freertos_task_verification():
|
||
\"\"\"FreeRTOS任务验证示例\"\"\"
|
||
print("=== FreeRTOS任务验证示例 ===")
|
||
|
||
# FreeRTOS任务代码
|
||
freertos_code = \"\"\"
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
|
||
typedef struct {
|
||
int task_id;
|
||
int counter;
|
||
int max_count;
|
||
BaseType_t should_stop;
|
||
} TaskParameters_t;
|
||
|
||
void vCounterTask(void *pvParameters) {
|
||
TaskParameters_t *params = (TaskParameters_t *)pvParameters;
|
||
|
||
// 参数验证
|
||
configASSERT(params != NULL);
|
||
configASSERT(params->task_id > 0);
|
||
configASSERT(params->max_count > 0);
|
||
|
||
while (params->should_stop == pdFALSE) {
|
||
if (params->counter < params->max_count) {
|
||
params->counter++;
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(100));
|
||
}
|
||
|
||
vTaskDelete(NULL);
|
||
}
|
||
\"\"\"
|
||
|
||
# 保存代码到文件
|
||
with open("freertos_task_example.c", "w") as f:
|
||
f.write(freertos_code)
|
||
|
||
try:
|
||
# FreeRTOS验证规范
|
||
specification = \"\"\"
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
|
||
typedef struct {
|
||
int task_id;
|
||
int counter;
|
||
int max_count;
|
||
BaseType_t should_stop;
|
||
} TaskParameters_t;
|
||
|
||
void test_task_creation() {
|
||
TaskParameters_t params;
|
||
TaskHandle_t task_handle;
|
||
|
||
// 初始化参数
|
||
params.task_id = 1;
|
||
params.counter = 0;
|
||
params.max_count = 10;
|
||
params.should_stop = pdFALSE;
|
||
|
||
// 测试任务创建
|
||
BaseType_t result = xTaskCreate(
|
||
vCounterTask,
|
||
"CounterTask",
|
||
configMINIMAL_STACK_SIZE,
|
||
¶ms,
|
||
1,
|
||
&task_handle
|
||
);
|
||
|
||
__CPROVER_assert(result == pdPASS, "task_creation_success");
|
||
__CPROVER_assert(task_handle != NULL, "task_handle_not_null");
|
||
}
|
||
|
||
void test_parameter_validation() {
|
||
TaskParameters_t *params = (TaskParameters_t *)__CPROVER_allocate(sizeof(TaskParameters_t));
|
||
|
||
// 测试NULL指针检查
|
||
vCounterTask(params);
|
||
|
||
// 测试有效参数
|
||
params->task_id = __CPROVER_nondet_int();
|
||
params->max_count = __CPROVER_nondet_int();
|
||
__CPROVER_assume(params->task_id > 0);
|
||
__CPROVER_assume(params->max_count > 0);
|
||
|
||
vCounterTask(params);
|
||
}
|
||
\"\"\"
|
||
|
||
# 运行验证
|
||
runner = CBMCRunner()
|
||
result = await runner.run_verification(
|
||
function_metadata={"name": "vCounterTask"},
|
||
source_file="freertos_task_example.c",
|
||
specification=specification
|
||
)
|
||
|
||
print(f"FreeRTOS验证结果: {result.status}")
|
||
print(f"执行时间: {result.execution_time}s")
|
||
|
||
finally:
|
||
# 清理临时文件
|
||
if Path("freertos_task_example.c").exists():
|
||
Path("freertos_task_example.c").unlink()
|
||
|
||
|
||
async def main():
|
||
\"\"\"主函数\"\"\"
|
||
try:
|
||
await freertos_task_verification()
|
||
except Exception as e:
|
||
print(f"FreeRTOS示例执行失败: {e}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|
||
"""
|
||
}
|
||
|
||
for filename, content in examples.items():
|
||
with open(examples_dir / filename, 'w', encoding='utf-8') as f:
|
||
f.write(content)
|
||
|
||
def _generate_index(self):
|
||
"""生成索引文档"""
|
||
print("📑 生成索引文档...")
|
||
|
||
md_content = f"""# CodeDetect API 文档
|
||
|
||
> 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||
|
||
## 概述
|
||
|
||
CodeDetect是一个基于LLM和CBMC的C代码形式化验证系统。本文档提供了完整的API参考,包括REST API、WebSocket事件、内部模块API和配置参数。
|
||
|
||
## 文档结构
|
||
|
||
### 1. [REST API](rest-api.md)
|
||
- API端点列表
|
||
- 请求/响应格式
|
||
- 认证和权限
|
||
- 示例代码
|
||
|
||
### 2. [WebSocket API](websocket-api.md)
|
||
- 实时通信事件
|
||
- 消息格式
|
||
- 连接管理
|
||
- 示例代码
|
||
|
||
### 3. [内部模块 API](internal-api.md)
|
||
- 核心模块接口
|
||
- 类和函数说明
|
||
- 使用示例
|
||
- 扩展开发
|
||
|
||
### 4. [配置参数](configuration.md)
|
||
- 系统配置
|
||
- 环境变量
|
||
- 参数说明
|
||
- 最佳实践
|
||
|
||
### 5. [示例代码](examples/)
|
||
- 基础使用示例
|
||
- FreeRTOS验证示例
|
||
- 自定义扩展示例
|
||
|
||
## 快速开始
|
||
|
||
### 安装依赖
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 基础使用
|
||
|
||
```python
|
||
from src.verify.cbmc_runner import CBMCRunner
|
||
from src.mutate.engine import MutationEngine
|
||
|
||
# 创建验证器
|
||
runner = CBMCRunner()
|
||
engine = MutationEngine()
|
||
|
||
# 运行验证
|
||
result = await runner.run_verification(
|
||
function_metadata={"name": "test_function"},
|
||
source_file="test.c",
|
||
specification="void test() { }"
|
||
)
|
||
```
|
||
|
||
## 贡献指南
|
||
|
||
如果您发现文档问题或想要贡献,请:
|
||
|
||
1. 查看源代码中的注释和文档字符串
|
||
2. 运行文档生成脚本
|
||
3. 提交Pull Request
|
||
|
||
## 技术支持
|
||
|
||
- 📧 邮箱: support@codedetect.com
|
||
- 📖 文档: [在线文档](https://docs.codedetect.com)
|
||
- 🐛 问题报告: [GitHub Issues](https://github.com/codedetect/issues)
|
||
|
||
---
|
||
*本文档由自动生成工具创建*
|
||
"""
|
||
|
||
# 保存索引文档
|
||
with open(self.output_dir / "README.md", 'w', encoding='utf-8') as f:
|
||
f.write(md_content)
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='CodeDetect API文档生成工具')
|
||
parser.add_argument('--project-root', type=str, default='.',
|
||
help='项目根目录 (默认: .)')
|
||
parser.add_argument('--output-dir', type=str, default='docs/api',
|
||
help='输出目录 (默认: docs/api)')
|
||
parser.add_argument('--verbose', action='store_true',
|
||
help='详细输出')
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 生成文档
|
||
generator = APIDocGenerator(args.project_root, args.output_dir)
|
||
generator.generate_all_docs()
|
||
|
||
print(f"\n📚 文档已生成到: {args.output_dir}")
|
||
print("📖 开始阅读: docs/api/README.md")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |