auto-coder 0.1.398__py3-none-any.whl → 0.1.400__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- auto_coder-0.1.400.dist-info/METADATA +396 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/RECORD +82 -29
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/entry_points.txt +2 -0
- autocoder/agent/base_agentic/base_agent.py +2 -2
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
- autocoder/agent/entry_command_agent/__init__.py +29 -0
- autocoder/agent/entry_command_agent/auto_tool.py +61 -0
- autocoder/agent/entry_command_agent/chat.py +475 -0
- autocoder/agent/entry_command_agent/designer.py +53 -0
- autocoder/agent/entry_command_agent/generate_command.py +50 -0
- autocoder/agent/entry_command_agent/project_reader.py +58 -0
- autocoder/agent/entry_command_agent/voice2text.py +71 -0
- autocoder/auto_coder.py +23 -548
- autocoder/auto_coder_runner.py +511 -8
- autocoder/chat/rules_command.py +1 -1
- autocoder/chat_auto_coder.py +6 -1
- autocoder/common/ac_style_command_parser/__init__.py +15 -0
- autocoder/common/ac_style_command_parser/example.py +7 -0
- autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +28 -45
- autocoder/common/ac_style_command_parser/test_parser.py +516 -0
- autocoder/common/auto_coder_lang.py +78 -0
- autocoder/common/command_completer_v2.py +1 -1
- autocoder/common/command_file_manager/examples.py +22 -8
- autocoder/common/command_file_manager/manager.py +37 -6
- autocoder/common/conversations/get_conversation_manager.py +143 -0
- autocoder/common/conversations/manager.py +122 -11
- autocoder/common/conversations/storage/index_manager.py +89 -0
- autocoder/common/pull_requests/__init__.py +256 -0
- autocoder/common/pull_requests/base_provider.py +191 -0
- autocoder/common/pull_requests/config.py +66 -0
- autocoder/common/pull_requests/example.py +1 -0
- autocoder/common/pull_requests/exceptions.py +46 -0
- autocoder/common/pull_requests/manager.py +201 -0
- autocoder/common/pull_requests/models.py +164 -0
- autocoder/common/pull_requests/providers/__init__.py +23 -0
- autocoder/common/pull_requests/providers/gitcode_provider.py +19 -0
- autocoder/common/pull_requests/providers/gitee_provider.py +20 -0
- autocoder/common/pull_requests/providers/github_provider.py +214 -0
- autocoder/common/pull_requests/providers/gitlab_provider.py +29 -0
- autocoder/common/pull_requests/test_module.py +1 -0
- autocoder/common/pull_requests/utils.py +344 -0
- autocoder/common/tokens/__init__.py +62 -0
- autocoder/common/tokens/counter.py +211 -0
- autocoder/common/tokens/file_detector.py +105 -0
- autocoder/common/tokens/filters.py +111 -0
- autocoder/common/tokens/models.py +28 -0
- autocoder/common/v2/agent/agentic_edit.py +312 -85
- autocoder/common/v2/agent/agentic_edit_types.py +11 -0
- autocoder/common/v2/code_auto_generate_editblock.py +10 -2
- autocoder/dispacher/__init__.py +10 -0
- autocoder/rags.py +0 -27
- autocoder/run_context.py +1 -0
- autocoder/sdk/__init__.py +188 -0
- autocoder/sdk/cli/__init__.py +15 -0
- autocoder/sdk/cli/__main__.py +26 -0
- autocoder/sdk/cli/completion_wrapper.py +38 -0
- autocoder/sdk/cli/formatters.py +211 -0
- autocoder/sdk/cli/handlers.py +175 -0
- autocoder/sdk/cli/install_completion.py +301 -0
- autocoder/sdk/cli/main.py +286 -0
- autocoder/sdk/cli/options.py +73 -0
- autocoder/sdk/constants.py +102 -0
- autocoder/sdk/core/__init__.py +20 -0
- autocoder/sdk/core/auto_coder_core.py +880 -0
- autocoder/sdk/core/bridge.py +500 -0
- autocoder/sdk/example.py +0 -0
- autocoder/sdk/exceptions.py +72 -0
- autocoder/sdk/models/__init__.py +19 -0
- autocoder/sdk/models/messages.py +209 -0
- autocoder/sdk/models/options.py +196 -0
- autocoder/sdk/models/responses.py +311 -0
- autocoder/sdk/session/__init__.py +32 -0
- autocoder/sdk/session/session.py +106 -0
- autocoder/sdk/session/session_manager.py +56 -0
- autocoder/sdk/utils/__init__.py +24 -0
- autocoder/sdk/utils/formatters.py +216 -0
- autocoder/sdk/utils/io_utils.py +302 -0
- autocoder/sdk/utils/validators.py +287 -0
- autocoder/version.py +2 -1
- auto_coder-0.1.398.dist-info/METADATA +0 -111
- autocoder/common/conversations/compatibility.py +0 -303
- autocoder/common/conversations/conversation_manager.py +0 -502
- autocoder/common/conversations/example.py +0 -152
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info/licenses}/LICENSE +0 -0
- {auto_coder-0.1.398.dist-info → auto_coder-0.1.400.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
Auto-Coder SDK 格式化工具
|
|
21
|
+
|
|
22
|
+
提供各种输出格式化功能。
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
from typing import Any, AsyncIterator, Dict
|
|
27
|
+
|
|
28
|
+
from ..models.messages import Message
|
|
29
|
+
from ..models.responses import StreamEvent
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def format_output(content: Any, output_format: str = "text") -> str:
|
|
33
|
+
"""
|
|
34
|
+
格式化输出内容
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
content: 要格式化的内容
|
|
38
|
+
output_format: 输出格式 (text, json)
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
str: 格式化后的内容
|
|
42
|
+
"""
|
|
43
|
+
if output_format == "json":
|
|
44
|
+
if isinstance(content, (dict, list)):
|
|
45
|
+
return json.dumps(content, ensure_ascii=False, indent=2)
|
|
46
|
+
elif hasattr(content, 'to_dict'):
|
|
47
|
+
return json.dumps(content.to_dict(), ensure_ascii=False, indent=2)
|
|
48
|
+
else:
|
|
49
|
+
return json.dumps({"content": str(content)}, ensure_ascii=False, indent=2)
|
|
50
|
+
else:
|
|
51
|
+
# text format
|
|
52
|
+
return str(content)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def format_stream_output(stream: AsyncIterator[Any], output_format: str = "stream-json") -> AsyncIterator[str]:
|
|
56
|
+
"""
|
|
57
|
+
格式化流式输出
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
stream: 输入流
|
|
61
|
+
output_format: 输出格式
|
|
62
|
+
|
|
63
|
+
Yields:
|
|
64
|
+
str: 格式化后的内容
|
|
65
|
+
"""
|
|
66
|
+
if output_format == "stream-json":
|
|
67
|
+
# 发送开始事件
|
|
68
|
+
start_event = StreamEvent.start_event()
|
|
69
|
+
yield start_event.to_json()
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
async for item in stream:
|
|
73
|
+
if isinstance(item, Message):
|
|
74
|
+
# 转换Message为StreamEvent
|
|
75
|
+
if item.is_assistant_message():
|
|
76
|
+
content_event = StreamEvent.content_event(item.content)
|
|
77
|
+
yield content_event.to_json()
|
|
78
|
+
elif hasattr(item, 'to_dict'):
|
|
79
|
+
# 直接输出有to_dict方法的对象
|
|
80
|
+
yield json.dumps(item.to_dict(), ensure_ascii=False)
|
|
81
|
+
else:
|
|
82
|
+
# 包装普通内容为content事件
|
|
83
|
+
content_event = StreamEvent.content_event(str(item))
|
|
84
|
+
yield content_event.to_json()
|
|
85
|
+
|
|
86
|
+
# 发送结束事件
|
|
87
|
+
end_event = StreamEvent.end_event()
|
|
88
|
+
yield end_event.to_json()
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
# 发送错误事件
|
|
92
|
+
error_event = StreamEvent.error_event(str(e))
|
|
93
|
+
yield error_event.to_json()
|
|
94
|
+
else:
|
|
95
|
+
# 其他格式,直接输出
|
|
96
|
+
async for item in stream:
|
|
97
|
+
yield format_output(item, "text")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def format_table_output(data: list, headers: list = None) -> str:
|
|
101
|
+
"""
|
|
102
|
+
格式化表格输出
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
data: 表格数据
|
|
106
|
+
headers: 表头
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
str: 格式化的表格
|
|
110
|
+
"""
|
|
111
|
+
if not data:
|
|
112
|
+
return "No data to display"
|
|
113
|
+
|
|
114
|
+
if headers is None:
|
|
115
|
+
if isinstance(data[0], dict):
|
|
116
|
+
headers = list(data[0].keys())
|
|
117
|
+
else:
|
|
118
|
+
headers = [f"Column {i+1}" for i in range(len(data[0]) if data[0] else 0)]
|
|
119
|
+
|
|
120
|
+
# 计算列宽
|
|
121
|
+
col_widths = []
|
|
122
|
+
for i, header in enumerate(headers):
|
|
123
|
+
max_width = len(header)
|
|
124
|
+
for row in data:
|
|
125
|
+
if isinstance(row, dict):
|
|
126
|
+
cell_value = str(row.get(header, ""))
|
|
127
|
+
elif isinstance(row, (list, tuple)) and i < len(row):
|
|
128
|
+
cell_value = str(row[i])
|
|
129
|
+
else:
|
|
130
|
+
cell_value = ""
|
|
131
|
+
max_width = max(max_width, len(cell_value))
|
|
132
|
+
col_widths.append(max_width + 2) # 添加padding
|
|
133
|
+
|
|
134
|
+
# 构建表格
|
|
135
|
+
lines = []
|
|
136
|
+
|
|
137
|
+
# 表头
|
|
138
|
+
header_line = "|".join(header.center(col_widths[i]) for i, header in enumerate(headers))
|
|
139
|
+
lines.append(header_line)
|
|
140
|
+
|
|
141
|
+
# 分隔线
|
|
142
|
+
separator = "|".join("-" * width for width in col_widths)
|
|
143
|
+
lines.append(separator)
|
|
144
|
+
|
|
145
|
+
# 数据行
|
|
146
|
+
for row in data:
|
|
147
|
+
if isinstance(row, dict):
|
|
148
|
+
row_values = [str(row.get(header, "")) for header in headers]
|
|
149
|
+
elif isinstance(row, (list, tuple)):
|
|
150
|
+
row_values = [str(row[i]) if i < len(row) else "" for i in range(len(headers))]
|
|
151
|
+
else:
|
|
152
|
+
row_values = [str(row)]
|
|
153
|
+
|
|
154
|
+
row_line = "|".join(value.ljust(col_widths[i]) for i, value in enumerate(row_values))
|
|
155
|
+
lines.append(row_line)
|
|
156
|
+
|
|
157
|
+
return "\n".join(lines)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def format_error_output(error: Exception, verbose: bool = False) -> str:
|
|
161
|
+
"""
|
|
162
|
+
格式化错误输出
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
error: 异常对象
|
|
166
|
+
verbose: 是否显示详细信息
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
str: 格式化的错误信息
|
|
170
|
+
"""
|
|
171
|
+
if verbose:
|
|
172
|
+
import traceback
|
|
173
|
+
return f"Error: {str(error)}\n\nTraceback:\n{traceback.format_exc()}"
|
|
174
|
+
else:
|
|
175
|
+
return f"Error: {str(error)}"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def format_progress_output(current: int, total: int, description: str = "") -> str:
|
|
179
|
+
"""
|
|
180
|
+
格式化进度输出
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
current: 当前进度
|
|
184
|
+
total: 总数
|
|
185
|
+
description: 描述
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
str: 格式化的进度信息
|
|
189
|
+
"""
|
|
190
|
+
if total == 0:
|
|
191
|
+
percentage = 100
|
|
192
|
+
else:
|
|
193
|
+
percentage = min(100, int((current / total) * 100))
|
|
194
|
+
|
|
195
|
+
bar_length = 30
|
|
196
|
+
filled_length = int(bar_length * percentage // 100)
|
|
197
|
+
bar = "█" * filled_length + "-" * (bar_length - filled_length)
|
|
198
|
+
|
|
199
|
+
return f"{description} [{bar}] {percentage}% ({current}/{total})"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
Auto-Coder SDK IO工具
|
|
24
|
+
|
|
25
|
+
提供输入输出相关的工具函数。
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import sys
|
|
29
|
+
import os
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Optional
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def read_stdin() -> str:
|
|
35
|
+
"""
|
|
36
|
+
从标准输入读取内容
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: 读取的内容
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
if sys.stdin.isatty():
|
|
43
|
+
# 如果是交互式终端,返回空字符串
|
|
44
|
+
return ""
|
|
45
|
+
else:
|
|
46
|
+
# 从管道或重定向读取
|
|
47
|
+
return sys.stdin.read().strip()
|
|
48
|
+
except Exception:
|
|
49
|
+
return ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def write_stdout(content: str) -> None:
|
|
53
|
+
"""
|
|
54
|
+
写入标准输出
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
content: 要写入的内容
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
sys.stdout.write(content)
|
|
61
|
+
sys.stdout.flush()
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def write_stderr(content: str) -> None:
|
|
67
|
+
"""
|
|
68
|
+
写入标准错误输出
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
content: 要写入的内容
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
sys.stderr.write(content)
|
|
75
|
+
sys.stderr.flush()
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def ensure_directory(dir_path: str) -> bool:
|
|
81
|
+
"""
|
|
82
|
+
确保目录存在,如果不存在则创建
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
dir_path: 目录路径
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
bool: 是否成功创建或已存在
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
Path(dir_path).mkdir(parents=True, exist_ok=True)
|
|
92
|
+
return True
|
|
93
|
+
except Exception:
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def read_file_safe(file_path: str, encoding: str = "utf-8") -> Optional[str]:
|
|
98
|
+
"""
|
|
99
|
+
安全读取文件内容
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
file_path: 文件路径
|
|
103
|
+
encoding: 编码格式
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Optional[str]: 文件内容,如果读取失败返回None
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
with open(file_path, 'r', encoding=encoding) as f:
|
|
110
|
+
return f.read()
|
|
111
|
+
except Exception:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def write_file_safe(file_path: str, content: str, encoding: str = "utf-8") -> bool:
|
|
116
|
+
"""
|
|
117
|
+
安全写入文件内容
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
file_path: 文件路径
|
|
121
|
+
content: 要写入的内容
|
|
122
|
+
encoding: 编码格式
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
bool: 是否写入成功
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
# 确保目录存在
|
|
129
|
+
ensure_directory(os.path.dirname(file_path))
|
|
130
|
+
|
|
131
|
+
with open(file_path, 'w', encoding=encoding) as f:
|
|
132
|
+
f.write(content)
|
|
133
|
+
return True
|
|
134
|
+
except Exception:
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def append_file_safe(file_path: str, content: str, encoding: str = "utf-8") -> bool:
|
|
139
|
+
"""
|
|
140
|
+
安全追加文件内容
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
file_path: 文件路径
|
|
144
|
+
content: 要追加的内容
|
|
145
|
+
encoding: 编码格式
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
bool: 是否追加成功
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
# 确保目录存在
|
|
152
|
+
ensure_directory(os.path.dirname(file_path))
|
|
153
|
+
|
|
154
|
+
with open(file_path, 'a', encoding=encoding) as f:
|
|
155
|
+
f.write(content)
|
|
156
|
+
return True
|
|
157
|
+
except Exception:
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def file_exists(file_path: str) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
检查文件是否存在
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
file_path: 文件路径
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: 文件是否存在
|
|
170
|
+
"""
|
|
171
|
+
return Path(file_path).exists() and Path(file_path).is_file()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def directory_exists(dir_path: str) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
检查目录是否存在
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
dir_path: 目录路径
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
bool: 目录是否存在
|
|
183
|
+
"""
|
|
184
|
+
return Path(dir_path).exists() and Path(dir_path).is_dir()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_file_size(file_path: str) -> Optional[int]:
|
|
188
|
+
"""
|
|
189
|
+
获取文件大小
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
file_path: 文件路径
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Optional[int]: 文件大小(字节),如果文件不存在返回None
|
|
196
|
+
"""
|
|
197
|
+
try:
|
|
198
|
+
return Path(file_path).stat().st_size
|
|
199
|
+
except Exception:
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def list_files_in_directory(dir_path: str, pattern: str = "*") -> list:
|
|
204
|
+
"""
|
|
205
|
+
列出目录中的文件
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
dir_path: 目录路径
|
|
209
|
+
pattern: 文件名模式
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
list: 文件路径列表
|
|
213
|
+
"""
|
|
214
|
+
try:
|
|
215
|
+
dir_path_obj = Path(dir_path)
|
|
216
|
+
if not dir_path_obj.exists() or not dir_path_obj.is_dir():
|
|
217
|
+
return []
|
|
218
|
+
|
|
219
|
+
return [str(path) for path in dir_path_obj.glob(pattern) if path.is_file()]
|
|
220
|
+
except Exception:
|
|
221
|
+
return []
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def create_temp_file(content: str = "", suffix: str = ".tmp") -> Optional[str]:
|
|
225
|
+
"""
|
|
226
|
+
创建临时文件
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
content: 文件内容
|
|
230
|
+
suffix: 文件后缀
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Optional[str]: 临时文件路径,如果创建失败返回None
|
|
234
|
+
"""
|
|
235
|
+
try:
|
|
236
|
+
import tempfile
|
|
237
|
+
|
|
238
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False) as f:
|
|
239
|
+
f.write(content)
|
|
240
|
+
return f.name
|
|
241
|
+
except Exception:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def delete_file_safe(file_path: str) -> bool:
|
|
246
|
+
"""
|
|
247
|
+
安全删除文件
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
file_path: 文件路径
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
bool: 是否删除成功
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
Path(file_path).unlink()
|
|
257
|
+
return True
|
|
258
|
+
except Exception:
|
|
259
|
+
return False
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def copy_file_safe(src_path: str, dst_path: str) -> bool:
|
|
263
|
+
"""
|
|
264
|
+
安全复制文件
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
src_path: 源文件路径
|
|
268
|
+
dst_path: 目标文件路径
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
bool: 是否复制成功
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
import shutil
|
|
275
|
+
|
|
276
|
+
# 确保目标目录存在
|
|
277
|
+
ensure_directory(os.path.dirname(dst_path))
|
|
278
|
+
|
|
279
|
+
shutil.copy2(src_path, dst_path)
|
|
280
|
+
return True
|
|
281
|
+
except Exception:
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|