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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import Dict, List, Tuple, Any, Optional
|
|
2
1
|
import re
|
|
2
|
+
from typing import Dict, List, Tuple, Any, Optional
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class CommandParser:
|
|
@@ -22,8 +22,8 @@ class CommandParser:
|
|
|
22
22
|
# 匹配命令的正则表达式 - 必须是以/开头,后跟单词字符,且不能后跟/或.
|
|
23
23
|
# (?<!\S) 确保命令前是字符串开头或空白字符
|
|
24
24
|
self.command_pattern = r'(?<!\S)/(\w+)(?!/|\.)'
|
|
25
|
-
#
|
|
26
|
-
self.key_value_pattern = r'(\w+)=(
|
|
25
|
+
# 匹配键值对参数的正则表达式,支持带引号的值(包括三重引号)
|
|
26
|
+
self.key_value_pattern = r'(\w+)=(?:\'\'\'([^\']*?)\'\'\'|"""([^"]*?)"""|"([^"]*?)"|\'([^\']*?)\'|([^\s"\']*))(?:\s|$)'
|
|
27
27
|
# 匹配路径模式的正则表达式
|
|
28
28
|
self.path_pattern = r'/\w+(?:/[^/\s]+)+'
|
|
29
29
|
|
|
@@ -119,8 +119,9 @@ class CommandParser:
|
|
|
119
119
|
if key_value_pairs:
|
|
120
120
|
for match in key_value_pairs:
|
|
121
121
|
key = match[0]
|
|
122
|
-
#
|
|
123
|
-
|
|
122
|
+
# 值可能在六个捕获组中的一个,取非空的那个
|
|
123
|
+
# match[1]: 三重单引号, match[2]: 三重双引号, match[3]: 双引号, match[4]: 单引号, match[5]: 无引号
|
|
124
|
+
value = match[1] or match[2] or match[3] or match[4] or match[5]
|
|
124
125
|
kwargs[key] = value.strip()
|
|
125
126
|
|
|
126
127
|
# 替换带引号的键值对
|
|
@@ -131,15 +132,22 @@ class CommandParser:
|
|
|
131
132
|
|
|
132
133
|
# 现在 processed_params_str 中应该只剩下位置参数
|
|
133
134
|
|
|
134
|
-
#
|
|
135
|
-
quote_pattern = r'(
|
|
135
|
+
# 处理带引号的位置参数(包括三重引号)
|
|
136
|
+
quote_pattern = r'(?:\'\'\'([^\']*?)\'\'\'|"""([^"]*?)"""|"([^"]*?)"|\'([^\']*?)\')'
|
|
136
137
|
quoted_args = re.findall(quote_pattern, processed_params_str)
|
|
137
138
|
for quoted_arg in quoted_args:
|
|
138
139
|
# 取非空的那个捕获组
|
|
139
|
-
arg = quoted_arg[0] or quoted_arg[1]
|
|
140
|
+
arg = quoted_arg[0] or quoted_arg[1] or quoted_arg[2] or quoted_arg[3]
|
|
140
141
|
args.append(arg)
|
|
141
142
|
# 从参数字符串中移除这个带引号的参数
|
|
142
|
-
|
|
143
|
+
if quoted_arg[0]: # 三重单引号
|
|
144
|
+
quoted_pattern = f"'''{arg}'''"
|
|
145
|
+
elif quoted_arg[1]: # 三重双引号
|
|
146
|
+
quoted_pattern = f'"""{arg}"""'
|
|
147
|
+
elif quoted_arg[2]: # 双引号
|
|
148
|
+
quoted_pattern = f'"{arg}"'
|
|
149
|
+
else: # 单引号
|
|
150
|
+
quoted_pattern = f"'{arg}'"
|
|
143
151
|
processed_params_str = processed_params_str.replace(quoted_pattern, "", 1).strip()
|
|
144
152
|
|
|
145
153
|
# 分割剩余的位置参数(不带引号的)
|
|
@@ -148,17 +156,24 @@ class CommandParser:
|
|
|
148
156
|
else:
|
|
149
157
|
# 如果没有键值对,处理所有参数作为位置参数
|
|
150
158
|
|
|
151
|
-
#
|
|
152
|
-
quote_pattern = r'(
|
|
159
|
+
# 处理带引号的位置参数(包括三重引号)
|
|
160
|
+
quote_pattern = r'(?:\'\'\'([^\']*?)\'\'\'|"""([^"]*?)"""|"([^"]*?)"|\'([^\']*?)\')'
|
|
153
161
|
quoted_args = re.findall(quote_pattern, params_str)
|
|
154
162
|
processed_params_str = params_str
|
|
155
163
|
|
|
156
164
|
for quoted_arg in quoted_args:
|
|
157
165
|
# 取非空的那个捕获组
|
|
158
|
-
arg = quoted_arg[0] or quoted_arg[1]
|
|
166
|
+
arg = quoted_arg[0] or quoted_arg[1] or quoted_arg[2] or quoted_arg[3]
|
|
159
167
|
args.append(arg)
|
|
160
168
|
# 从参数字符串中移除这个带引号的参数
|
|
161
|
-
|
|
169
|
+
if quoted_arg[0]: # 三重单引号
|
|
170
|
+
quoted_pattern = f"'''{arg}'''"
|
|
171
|
+
elif quoted_arg[1]: # 三重双引号
|
|
172
|
+
quoted_pattern = f'"""{arg}"""'
|
|
173
|
+
elif quoted_arg[2]: # 双引号
|
|
174
|
+
quoted_pattern = f'"{arg}"'
|
|
175
|
+
else: # 单引号
|
|
176
|
+
quoted_pattern = f"'{arg}'"
|
|
162
177
|
processed_params_str = processed_params_str.replace(quoted_pattern, "", 1).strip()
|
|
163
178
|
|
|
164
179
|
# 分割剩余的位置参数(不带引号的)
|
|
@@ -246,35 +261,3 @@ def get_command_kwargs(query: str, command: str) -> Dict[str, str]:
|
|
|
246
261
|
if command_info:
|
|
247
262
|
return command_info['kwargs']
|
|
248
263
|
return {}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# 示例用法
|
|
252
|
-
if __name__ == "__main__":
|
|
253
|
-
# 测试各种格式的查询
|
|
254
|
-
test_queries = [
|
|
255
|
-
"/learn hello world /commit 123456",
|
|
256
|
-
"/learn /commit 123456",
|
|
257
|
-
"/learn /commit commit_id=123456",
|
|
258
|
-
"/learn msg=hello /commit commit_id=123456",
|
|
259
|
-
"/learn hello key=value /commit",
|
|
260
|
-
# 带引号的值
|
|
261
|
-
'/learn msg="hello world" /commit message="Fix bug #123"',
|
|
262
|
-
"/learn 'quoted arg' key='value with spaces' /commit",
|
|
263
|
-
# 路径参数测试
|
|
264
|
-
"/learn /path/to/file.txt",
|
|
265
|
-
"/commit message='Added /path/to/file.txt'",
|
|
266
|
-
"Check /path/to/file.txt and also /another/path/file.md",
|
|
267
|
-
"/clone /path/to/repo /checkout branch",
|
|
268
|
-
"Use the file at /usr/local/bin/python with /learn"
|
|
269
|
-
]
|
|
270
|
-
|
|
271
|
-
for query in test_queries:
|
|
272
|
-
print(f"\nQuery: {query}")
|
|
273
|
-
result = parse_query(query)
|
|
274
|
-
print(f"Parsed: {result}")
|
|
275
|
-
|
|
276
|
-
if has_command(query, "commit"):
|
|
277
|
-
args = get_command_args(query, "commit")
|
|
278
|
-
kwargs = get_command_kwargs(query, "commit")
|
|
279
|
-
print(f"Commit args: {args}")
|
|
280
|
-
print(f"Commit kwargs: {kwargs}")
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
|
|
2
|
+
import pytest
|
|
3
|
+
from typing import Dict, List, Any
|
|
4
|
+
|
|
5
|
+
# 导入被测模块
|
|
6
|
+
from .parser import (
|
|
7
|
+
CommandParser,
|
|
8
|
+
parse_query,
|
|
9
|
+
has_command,
|
|
10
|
+
get_command_args,
|
|
11
|
+
get_command_kwargs
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestCommandParser:
|
|
16
|
+
"""CommandParser类的单元测试"""
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def parser(self):
|
|
20
|
+
"""提供CommandParser实例"""
|
|
21
|
+
return CommandParser()
|
|
22
|
+
|
|
23
|
+
def test_init(self, parser):
|
|
24
|
+
"""测试CommandParser初始化"""
|
|
25
|
+
assert parser is not None
|
|
26
|
+
assert hasattr(parser, 'command_pattern')
|
|
27
|
+
assert hasattr(parser, 'key_value_pattern')
|
|
28
|
+
assert hasattr(parser, 'path_pattern')
|
|
29
|
+
|
|
30
|
+
def test_empty_query(self, parser):
|
|
31
|
+
"""测试空查询字符串"""
|
|
32
|
+
assert parser.parse("") == {}
|
|
33
|
+
assert parser.parse(" ") == {}
|
|
34
|
+
assert parser.parse(None) == {}
|
|
35
|
+
|
|
36
|
+
def test_single_command_no_args(self, parser):
|
|
37
|
+
"""测试单个命令无参数"""
|
|
38
|
+
result = parser.parse("/help")
|
|
39
|
+
expected = {
|
|
40
|
+
"help": {
|
|
41
|
+
"args": [],
|
|
42
|
+
"kwargs": {}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
assert result == expected
|
|
46
|
+
|
|
47
|
+
def test_single_command_with_args(self, parser):
|
|
48
|
+
"""测试单个命令带位置参数"""
|
|
49
|
+
result = parser.parse("/add file1.txt file2.py")
|
|
50
|
+
expected = {
|
|
51
|
+
"add": {
|
|
52
|
+
"args": ["file1.txt", "file2.py"],
|
|
53
|
+
"kwargs": {}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
assert result == expected
|
|
57
|
+
|
|
58
|
+
def test_single_command_with_kwargs(self, parser):
|
|
59
|
+
"""测试单个命令带键值对参数"""
|
|
60
|
+
result = parser.parse("/config model=gpt-4 temperature=0.7")
|
|
61
|
+
expected = {
|
|
62
|
+
"config": {
|
|
63
|
+
"args": [],
|
|
64
|
+
"kwargs": {
|
|
65
|
+
"model": "gpt-4",
|
|
66
|
+
"temperature": "0.7"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
assert result == expected
|
|
71
|
+
|
|
72
|
+
def test_single_command_mixed_params(self, parser):
|
|
73
|
+
"""测试单个命令混合参数"""
|
|
74
|
+
result = parser.parse("/deploy myapp env=prod version=1.0")
|
|
75
|
+
expected = {
|
|
76
|
+
"deploy": {
|
|
77
|
+
"args": ["myapp"],
|
|
78
|
+
"kwargs": {
|
|
79
|
+
"env": "prod",
|
|
80
|
+
"version": "1.0"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
assert result == expected
|
|
85
|
+
|
|
86
|
+
def test_quoted_args_double_quotes(self, parser):
|
|
87
|
+
"""测试双引号参数"""
|
|
88
|
+
result = parser.parse('/say "hello world" "this is a test"')
|
|
89
|
+
expected = {
|
|
90
|
+
"say": {
|
|
91
|
+
"args": ["hello world", "this is a test"],
|
|
92
|
+
"kwargs": {}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
assert result == expected
|
|
96
|
+
|
|
97
|
+
def test_quoted_args_single_quotes(self, parser):
|
|
98
|
+
"""测试单引号参数"""
|
|
99
|
+
result = parser.parse("/say 'hello world' 'this is a test'")
|
|
100
|
+
expected = {
|
|
101
|
+
"say": {
|
|
102
|
+
"args": ["hello world", "this is a test"],
|
|
103
|
+
"kwargs": {}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
assert result == expected
|
|
107
|
+
|
|
108
|
+
def test_quoted_kwargs(self, parser):
|
|
109
|
+
"""测试带引号的键值对参数"""
|
|
110
|
+
result = parser.parse('/config message="hello world" path=\'/tmp/test dir\'')
|
|
111
|
+
expected = {
|
|
112
|
+
"config": {
|
|
113
|
+
"args": [],
|
|
114
|
+
"kwargs": {
|
|
115
|
+
"message": "hello world",
|
|
116
|
+
"path": "/tmp/test dir"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
assert result == expected
|
|
121
|
+
|
|
122
|
+
def test_multiple_commands(self, parser):
|
|
123
|
+
"""测试多个命令"""
|
|
124
|
+
result = parser.parse("/add file1.txt /remove file2.txt")
|
|
125
|
+
expected = {
|
|
126
|
+
"add": {
|
|
127
|
+
"args": ["file1.txt"],
|
|
128
|
+
"kwargs": {}
|
|
129
|
+
},
|
|
130
|
+
"remove": {
|
|
131
|
+
"args": ["file2.txt"],
|
|
132
|
+
"kwargs": {}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
assert result == expected
|
|
136
|
+
|
|
137
|
+
def test_multiple_commands_with_mixed_params(self, parser):
|
|
138
|
+
"""测试多个命令带混合参数"""
|
|
139
|
+
result = parser.parse("/deploy app1 env=prod /config model=gpt-4 /status")
|
|
140
|
+
expected = {
|
|
141
|
+
"deploy": {
|
|
142
|
+
"args": ["app1"],
|
|
143
|
+
"kwargs": {"env": "prod"}
|
|
144
|
+
},
|
|
145
|
+
"config": {
|
|
146
|
+
"args": [],
|
|
147
|
+
"kwargs": {"model": "gpt-4"}
|
|
148
|
+
},
|
|
149
|
+
"status": {
|
|
150
|
+
"args": [],
|
|
151
|
+
"kwargs": {}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
assert result == expected
|
|
155
|
+
|
|
156
|
+
def test_path_not_recognized_as_command(self, parser):
|
|
157
|
+
"""测试路径不被识别为命令"""
|
|
158
|
+
result = parser.parse("/add /path/to/file.txt /config model=gpt-4")
|
|
159
|
+
expected = {
|
|
160
|
+
"add": {
|
|
161
|
+
"args": ["/path/to/file.txt"],
|
|
162
|
+
"kwargs": {}
|
|
163
|
+
},
|
|
164
|
+
"config": {
|
|
165
|
+
"args": [],
|
|
166
|
+
"kwargs": {"model": "gpt-4"}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
assert result == expected
|
|
170
|
+
|
|
171
|
+
def test_complex_path_handling(self, parser):
|
|
172
|
+
"""测试复杂路径处理"""
|
|
173
|
+
result = parser.parse("/analyze /home/user/project/src/main.py /output /tmp/results")
|
|
174
|
+
expected = {
|
|
175
|
+
"analyze": {
|
|
176
|
+
"args": ["/home/user/project/src/main.py"],
|
|
177
|
+
"kwargs": {}
|
|
178
|
+
},
|
|
179
|
+
"output": {
|
|
180
|
+
"args": ["/tmp/results"],
|
|
181
|
+
"kwargs": {}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
assert result == expected
|
|
185
|
+
|
|
186
|
+
def test_command_with_dots_not_recognized(self, parser):
|
|
187
|
+
"""测试带点的字符串不被识别为命令"""
|
|
188
|
+
result = parser.parse("check /config.json /setup.py")
|
|
189
|
+
# 不以/开头,所以没有命令被识别
|
|
190
|
+
assert result == {}
|
|
191
|
+
|
|
192
|
+
def test_parse_command_existing(self, parser):
|
|
193
|
+
"""测试解析特定存在的命令"""
|
|
194
|
+
query = "/add file1.txt /remove file2.txt mode=force"
|
|
195
|
+
result = parser.parse_command(query, "remove")
|
|
196
|
+
expected = {
|
|
197
|
+
"args": ["file2.txt"],
|
|
198
|
+
"kwargs": {"mode": "force"}
|
|
199
|
+
}
|
|
200
|
+
assert result == expected
|
|
201
|
+
|
|
202
|
+
def test_parse_command_nonexistent(self, parser):
|
|
203
|
+
"""测试解析不存在的命令"""
|
|
204
|
+
query = "/add file1.txt /remove file2.txt"
|
|
205
|
+
result = parser.parse_command(query, "deploy")
|
|
206
|
+
assert result is None
|
|
207
|
+
|
|
208
|
+
def test_edge_case_empty_kwargs_value(self, parser):
|
|
209
|
+
"""测试空的键值对值"""
|
|
210
|
+
result = parser.parse("/config key1= key2=value")
|
|
211
|
+
expected = {
|
|
212
|
+
"config": {
|
|
213
|
+
"args": [],
|
|
214
|
+
"kwargs": {
|
|
215
|
+
"key1": "",
|
|
216
|
+
"key2": "value"
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
assert result == expected
|
|
221
|
+
|
|
222
|
+
def test_edge_case_special_characters_in_values(self, parser):
|
|
223
|
+
"""测试值中包含特殊字符"""
|
|
224
|
+
result = parser.parse("/config url=https://api.example.com/v1 pattern='*.py'")
|
|
225
|
+
expected = {
|
|
226
|
+
"config": {
|
|
227
|
+
"args": [],
|
|
228
|
+
"kwargs": {
|
|
229
|
+
"url": "https://api.example.com/v1",
|
|
230
|
+
"pattern": "*.py"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
assert result == expected
|
|
235
|
+
|
|
236
|
+
def test_command_at_end_of_string(self, parser):
|
|
237
|
+
"""测试字符串末尾的命令"""
|
|
238
|
+
result = parser.parse("some text /help")
|
|
239
|
+
expected = {
|
|
240
|
+
"help": {
|
|
241
|
+
"args": [],
|
|
242
|
+
"kwargs": {}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
assert result == expected
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class TestConvenienceFunctions:
|
|
249
|
+
"""测试便捷函数"""
|
|
250
|
+
|
|
251
|
+
def test_parse_query_function(self):
|
|
252
|
+
"""测试parse_query函数"""
|
|
253
|
+
result = parse_query("/add file1.txt mode=fast")
|
|
254
|
+
expected = {
|
|
255
|
+
"add": {
|
|
256
|
+
"args": ["file1.txt"],
|
|
257
|
+
"kwargs": {"mode": "fast"}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
assert result == expected
|
|
261
|
+
|
|
262
|
+
def test_has_command_true(self):
|
|
263
|
+
"""测试has_command函数 - 命令存在"""
|
|
264
|
+
assert has_command("/add file1.txt /remove file2.txt", "add") == True
|
|
265
|
+
assert has_command("/add file1.txt /remove file2.txt", "remove") == True
|
|
266
|
+
|
|
267
|
+
def test_has_command_false(self):
|
|
268
|
+
"""测试has_command函数 - 命令不存在"""
|
|
269
|
+
assert has_command("/add file1.txt /remove file2.txt", "deploy") == False
|
|
270
|
+
assert has_command("no commands here", "add") == False
|
|
271
|
+
assert has_command("", "add") == False
|
|
272
|
+
|
|
273
|
+
def test_get_command_args_existing(self):
|
|
274
|
+
"""测试get_command_args函数 - 命令存在"""
|
|
275
|
+
query = "/add file1.txt file2.py /remove file3.txt"
|
|
276
|
+
assert get_command_args(query, "add") == ["file1.txt", "file2.py"]
|
|
277
|
+
assert get_command_args(query, "remove") == ["file3.txt"]
|
|
278
|
+
|
|
279
|
+
def test_get_command_args_nonexistent(self):
|
|
280
|
+
"""测试get_command_args函数 - 命令不存在"""
|
|
281
|
+
query = "/add file1.txt file2.py"
|
|
282
|
+
assert get_command_args(query, "remove") == []
|
|
283
|
+
assert get_command_args("", "add") == []
|
|
284
|
+
|
|
285
|
+
def test_get_command_kwargs_existing(self):
|
|
286
|
+
"""测试get_command_kwargs函数 - 命令存在"""
|
|
287
|
+
query = "/config model=gpt-4 temp=0.7 /deploy env=prod"
|
|
288
|
+
assert get_command_kwargs(query, "config") == {"model": "gpt-4", "temp": "0.7"}
|
|
289
|
+
assert get_command_kwargs(query, "deploy") == {"env": "prod"}
|
|
290
|
+
|
|
291
|
+
def test_get_command_kwargs_nonexistent(self):
|
|
292
|
+
"""测试get_command_kwargs函数 - 命令不存在"""
|
|
293
|
+
query = "/config model=gpt-4"
|
|
294
|
+
assert get_command_kwargs(query, "deploy") == {}
|
|
295
|
+
assert get_command_kwargs("", "config") == {}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TestComplexScenarios:
|
|
299
|
+
"""测试复杂场景"""
|
|
300
|
+
|
|
301
|
+
def test_real_world_scenario_1(self):
|
|
302
|
+
"""测试真实场景1:文件操作命令"""
|
|
303
|
+
query = '/add "src/main.py" "tests/test_main.py" /config model="gpt-4" temperature=0.7 /deploy env=production version="1.2.3"'
|
|
304
|
+
result = parse_query(query)
|
|
305
|
+
|
|
306
|
+
expected = {
|
|
307
|
+
"add": {
|
|
308
|
+
"args": ["src/main.py", "tests/test_main.py"],
|
|
309
|
+
"kwargs": {}
|
|
310
|
+
},
|
|
311
|
+
"config": {
|
|
312
|
+
"args": [],
|
|
313
|
+
"kwargs": {
|
|
314
|
+
"model": "gpt-4",
|
|
315
|
+
"temperature": "0.7"
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
"deploy": {
|
|
319
|
+
"args": [],
|
|
320
|
+
"kwargs": {
|
|
321
|
+
"env": "production",
|
|
322
|
+
"version": "1.2.3"
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
assert result == expected
|
|
327
|
+
|
|
328
|
+
def test_real_world_scenario_2(self):
|
|
329
|
+
"""测试真实场景2:包含路径的复杂命令"""
|
|
330
|
+
query = "/analyze /home/user/project/src /output /tmp/analysis.json format=json verbose=true"
|
|
331
|
+
result = parse_query(query)
|
|
332
|
+
|
|
333
|
+
expected = {
|
|
334
|
+
"analyze": {
|
|
335
|
+
"args": ["/home/user/project/src"],
|
|
336
|
+
"kwargs": {}
|
|
337
|
+
},
|
|
338
|
+
"output": {
|
|
339
|
+
"args": ["/tmp/analysis.json"],
|
|
340
|
+
"kwargs": {
|
|
341
|
+
"format": "json",
|
|
342
|
+
"verbose": "true"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
assert result == expected
|
|
347
|
+
|
|
348
|
+
def test_real_world_scenario_3(self):
|
|
349
|
+
"""测试真实场景3:混合引号和特殊字符"""
|
|
350
|
+
query = """/search pattern="*.py" path='/home/user/My Documents/project' /filter exclude="__pycache__" """
|
|
351
|
+
result = parse_query(query)
|
|
352
|
+
|
|
353
|
+
expected = {
|
|
354
|
+
"search": {
|
|
355
|
+
"args": [],
|
|
356
|
+
"kwargs": {
|
|
357
|
+
"pattern": "*.py",
|
|
358
|
+
"path": "/home/user/My Documents/project"
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"filter": {
|
|
362
|
+
"args": [],
|
|
363
|
+
"kwargs": {
|
|
364
|
+
"exclude": "__pycache__"
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
assert result == expected
|
|
369
|
+
|
|
370
|
+
def test_edge_case_consecutive_commands(self):
|
|
371
|
+
"""测试连续命令"""
|
|
372
|
+
query = "/start /stop /restart mode=fast"
|
|
373
|
+
result = parse_query(query)
|
|
374
|
+
|
|
375
|
+
expected = {
|
|
376
|
+
"start": {
|
|
377
|
+
"args": [],
|
|
378
|
+
"kwargs": {}
|
|
379
|
+
},
|
|
380
|
+
"stop": {
|
|
381
|
+
"args": [],
|
|
382
|
+
"kwargs": {}
|
|
383
|
+
},
|
|
384
|
+
"restart": {
|
|
385
|
+
"args": [],
|
|
386
|
+
"kwargs": {"mode": "fast"}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
assert result == expected
|
|
390
|
+
|
|
391
|
+
def test_edge_case_command_with_numbers_and_underscores(self):
|
|
392
|
+
"""测试包含数字和下划线的命令"""
|
|
393
|
+
query = "/test_case_1 /deploy_v2 app_name=test123"
|
|
394
|
+
result = parse_query(query)
|
|
395
|
+
|
|
396
|
+
expected = {
|
|
397
|
+
"test_case_1": {
|
|
398
|
+
"args": [],
|
|
399
|
+
"kwargs": {}
|
|
400
|
+
},
|
|
401
|
+
"deploy_v2": {
|
|
402
|
+
"args": [],
|
|
403
|
+
"kwargs": {"app_name": "test123"}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
assert result == expected
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class TestErrorHandling:
|
|
410
|
+
"""测试错误处理和边界情况"""
|
|
411
|
+
|
|
412
|
+
def test_malformed_quotes(self):
|
|
413
|
+
"""测试格式错误的引号"""
|
|
414
|
+
# 这些应该仍能部分解析,不会抛出异常
|
|
415
|
+
parser = CommandParser()
|
|
416
|
+
|
|
417
|
+
# 未闭合的引号 - 应该被当作普通参数处理
|
|
418
|
+
result = parser.parse('/test "unclosed quote arg')
|
|
419
|
+
assert "test" in result
|
|
420
|
+
|
|
421
|
+
# 混合引号
|
|
422
|
+
result = parser.parse('/test "mixed\' quotes"')
|
|
423
|
+
assert "test" in result
|
|
424
|
+
|
|
425
|
+
def test_special_characters_in_commands(self):
|
|
426
|
+
"""测试命令中的特殊字符"""
|
|
427
|
+
parser = CommandParser()
|
|
428
|
+
|
|
429
|
+
# 命令名只能包含单词字符,所以这些不会被识别为命令
|
|
430
|
+
result = parser.parse("/test-command /test.command")
|
|
431
|
+
assert result == {}
|
|
432
|
+
|
|
433
|
+
def test_whitespace_handling(self):
|
|
434
|
+
"""测试空白字符处理"""
|
|
435
|
+
parser = CommandParser()
|
|
436
|
+
|
|
437
|
+
query = " /add file1.txt file2.py key=value "
|
|
438
|
+
result = parser.parse(query)
|
|
439
|
+
|
|
440
|
+
expected = {
|
|
441
|
+
"add": {
|
|
442
|
+
"args": ["file1.txt", "file2.py"],
|
|
443
|
+
"kwargs": {"key": "value"}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
assert result == expected
|
|
447
|
+
|
|
448
|
+
def test_unicode_characters(self):
|
|
449
|
+
"""测试Unicode字符"""
|
|
450
|
+
parser = CommandParser()
|
|
451
|
+
|
|
452
|
+
query = '/test "中文参数" key="中文值"'
|
|
453
|
+
result = parser.parse(query)
|
|
454
|
+
|
|
455
|
+
expected = {
|
|
456
|
+
"test": {
|
|
457
|
+
"args": ["中文参数"],
|
|
458
|
+
"kwargs": {"key": "中文值"}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
assert result == expected
|
|
462
|
+
|
|
463
|
+
def test__command_with_path(self, parser):
|
|
464
|
+
"""测试单个命令混合参数"""
|
|
465
|
+
result = parser.parse('/command "tdd/hello.md" name="威廉"')
|
|
466
|
+
expected = {
|
|
467
|
+
"command": {
|
|
468
|
+
"args": ["tdd/hello.md"],
|
|
469
|
+
"kwargs": {
|
|
470
|
+
"name": "威廉"
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
assert result == expected
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
# 参数化测试用例
|
|
478
|
+
@pytest.mark.parametrize("query,expected_commands", [
|
|
479
|
+
("/help", ["help"]),
|
|
480
|
+
("/add /remove", ["add", "remove"]),
|
|
481
|
+
("/config model=gpt-4", ["config"]),
|
|
482
|
+
("no commands", []),
|
|
483
|
+
("/start /stop /restart", ["start", "stop", "restart"]),
|
|
484
|
+
])
|
|
485
|
+
def test_command_detection_parametrized(query, expected_commands):
|
|
486
|
+
"""参数化测试命令检测"""
|
|
487
|
+
result = parse_query(query)
|
|
488
|
+
actual_commands = list(result.keys())
|
|
489
|
+
assert actual_commands == expected_commands
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@pytest.mark.parametrize("query,command,expected_args", [
|
|
493
|
+
("/add file1 file2", "add", ["file1", "file2"]),
|
|
494
|
+
("/remove", "remove", []),
|
|
495
|
+
("/test arg1 'arg with spaces'", "test", ["arg1", "arg with spaces"]),
|
|
496
|
+
("", "any", []),
|
|
497
|
+
])
|
|
498
|
+
def test_get_args_parametrized(query, command, expected_args):
|
|
499
|
+
"""参数化测试获取参数"""
|
|
500
|
+
assert get_command_args(query, command) == expected_args
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@pytest.mark.parametrize("query,command,expected_kwargs", [
|
|
504
|
+
("/config model=gpt-4 temp=0.7", "config", {"model": "gpt-4", "temp": "0.7"}),
|
|
505
|
+
("/deploy", "deploy", {}),
|
|
506
|
+
("/test key='value with spaces'", "test", {"key": "value with spaces"}),
|
|
507
|
+
("", "any", {}),
|
|
508
|
+
])
|
|
509
|
+
def test_get_kwargs_parametrized(query, command, expected_kwargs):
|
|
510
|
+
"""参数化测试获取键值对参数"""
|
|
511
|
+
assert get_command_kwargs(query, command) == expected_kwargs
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
if __name__ == "__main__":
|
|
515
|
+
# 可以直接运行此文件进行测试
|
|
516
|
+
pytest.main([__file__, "-v"])
|