pytest-dsl 0.4.0__py3-none-any.whl → 0.6.0__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.
- pytest_dsl/cli.py +28 -33
- pytest_dsl/core/auto_decorator.py +72 -53
- pytest_dsl/core/auto_directory.py +8 -5
- pytest_dsl/core/dsl_executor.py +211 -53
- pytest_dsl/core/http_request.py +272 -221
- pytest_dsl/core/lexer.py +14 -13
- pytest_dsl/core/parser.py +27 -8
- pytest_dsl/core/parsetab.py +71 -66
- pytest_dsl/core/plugin_discovery.py +1 -8
- pytest_dsl/core/yaml_loader.py +96 -19
- pytest_dsl/examples/assert/assertion_example.auto +1 -1
- pytest_dsl/examples/assert/boolean_test.auto +2 -2
- pytest_dsl/examples/assert/expression_test.auto +1 -1
- pytest_dsl/examples/custom/test_advanced_keywords.auto +2 -2
- pytest_dsl/examples/custom/test_custom_keywords.auto +2 -2
- pytest_dsl/examples/custom/test_default_values.auto +2 -2
- pytest_dsl/examples/http/file_reference_test.auto +1 -1
- pytest_dsl/examples/http/http_advanced.auto +1 -1
- pytest_dsl/examples/http/http_example.auto +1 -1
- pytest_dsl/examples/http/http_length_test.auto +1 -1
- pytest_dsl/examples/http/http_retry_assertions.auto +1 -1
- pytest_dsl/examples/http/http_retry_assertions_enhanced.auto +2 -2
- pytest_dsl/examples/http/http_with_yaml.auto +1 -1
- pytest_dsl/examples/quickstart/api_basics.auto +1 -1
- pytest_dsl/examples/quickstart/assertions.auto +1 -1
- pytest_dsl/examples/quickstart/loops.auto +2 -2
- pytest_dsl/keywords/assertion_keywords.py +76 -62
- pytest_dsl/keywords/global_keywords.py +43 -4
- pytest_dsl/keywords/http_keywords.py +141 -139
- {pytest_dsl-0.4.0.dist-info → pytest_dsl-0.6.0.dist-info}/METADATA +266 -15
- pytest_dsl-0.6.0.dist-info/RECORD +68 -0
- {pytest_dsl-0.4.0.dist-info → pytest_dsl-0.6.0.dist-info}/WHEEL +1 -1
- {pytest_dsl-0.4.0.dist-info → pytest_dsl-0.6.0.dist-info}/entry_points.txt +1 -0
- pytest_dsl-0.4.0.dist-info/RECORD +0 -68
- {pytest_dsl-0.4.0.dist-info → pytest_dsl-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.4.0.dist-info → pytest_dsl-0.6.0.dist-info}/top_level.txt +0 -0
pytest_dsl/core/dsl_executor.py
CHANGED
@@ -27,7 +27,7 @@ class DSLExecutor:
|
|
27
27
|
self.test_context.executor = self # 让 test_context 能够访问到 executor
|
28
28
|
self.variable_replacer = VariableReplacer(self.variables, self.test_context)
|
29
29
|
self.imported_files = set() # 跟踪已导入的文件,避免循环导入
|
30
|
-
|
30
|
+
|
31
31
|
def set_current_data(self, data):
|
32
32
|
"""设置当前测试数据集"""
|
33
33
|
if data:
|
@@ -35,30 +35,30 @@ class DSLExecutor:
|
|
35
35
|
# 同时将数据添加到测试上下文
|
36
36
|
for key, value in data.items():
|
37
37
|
self.test_context.set(key, value)
|
38
|
-
|
38
|
+
|
39
39
|
def _load_test_data(self, data_source):
|
40
40
|
"""加载测试数据
|
41
|
-
|
41
|
+
|
42
42
|
:param data_source: 数据源配置,包含 file 和 format 字段
|
43
43
|
:return: 包含测试数据的列表
|
44
44
|
"""
|
45
45
|
if not data_source:
|
46
46
|
return [{}] # 如果没有数据源,返回一个空的数据集
|
47
|
-
|
47
|
+
|
48
48
|
file_path = data_source['file']
|
49
49
|
format_type = data_source['format']
|
50
|
-
|
50
|
+
|
51
51
|
if not os.path.exists(file_path):
|
52
52
|
raise Exception(f"数据文件不存在: {file_path}")
|
53
|
-
|
53
|
+
|
54
54
|
if format_type.lower() == 'csv':
|
55
55
|
return self._load_csv_data(file_path)
|
56
56
|
else:
|
57
57
|
raise Exception(f"不支持的数据格式: {format_type}")
|
58
|
-
|
58
|
+
|
59
59
|
def _load_csv_data(self, file_path):
|
60
60
|
"""加载CSV格式的测试数据
|
61
|
-
|
61
|
+
|
62
62
|
:param file_path: CSV文件路径
|
63
63
|
:return: 包含测试数据的列表
|
64
64
|
"""
|
@@ -68,11 +68,11 @@ class DSLExecutor:
|
|
68
68
|
for row in reader:
|
69
69
|
data_sets.append(row)
|
70
70
|
return data_sets
|
71
|
-
|
71
|
+
|
72
72
|
def eval_expression(self, expr_node):
|
73
73
|
"""
|
74
74
|
对表达式节点进行求值,返回表达式的值。
|
75
|
-
|
75
|
+
|
76
76
|
:param expr_node: AST中的表达式节点
|
77
77
|
:return: 表达式求值后的结果
|
78
78
|
:raises Exception: 当遇到未定义变量或无法求值的类型时抛出异常
|
@@ -101,7 +101,7 @@ class DSLExecutor:
|
|
101
101
|
return self._eval_arithmetic_expr(expr_node)
|
102
102
|
else:
|
103
103
|
raise Exception(f"无法求值的表达式类型: {expr_node.type}")
|
104
|
-
|
104
|
+
|
105
105
|
def _eval_expression_value(self, value):
|
106
106
|
"""处理表达式值的具体逻辑"""
|
107
107
|
if isinstance(value, Node):
|
@@ -110,7 +110,7 @@ class DSLExecutor:
|
|
110
110
|
# 如果是ID类型的变量名
|
111
111
|
if value in self.variable_replacer.local_variables:
|
112
112
|
return self.variable_replacer.local_variables[value]
|
113
|
-
|
113
|
+
|
114
114
|
# 定义变量引用模式
|
115
115
|
pattern = r'\$\{([a-zA-Z_\u4e00-\u9fa5][a-zA-Z0-9_\u4e00-\u9fa5]*)\}'
|
116
116
|
# 检查整个字符串是否完全匹配单一变量引用模式
|
@@ -122,24 +122,24 @@ class DSLExecutor:
|
|
122
122
|
# 如果不是单一变量,则替换字符串中的所有变量引用
|
123
123
|
return self.variable_replacer.replace_in_string(value)
|
124
124
|
return value
|
125
|
-
|
125
|
+
|
126
126
|
def _eval_comparison_expr(self, expr_node):
|
127
127
|
"""
|
128
128
|
对比较表达式进行求值
|
129
|
-
|
129
|
+
|
130
130
|
:param expr_node: 比较表达式节点
|
131
131
|
:return: 比较结果(布尔值)
|
132
132
|
"""
|
133
133
|
left_value = self.eval_expression(expr_node.children[0])
|
134
134
|
right_value = self.eval_expression(expr_node.children[1])
|
135
135
|
operator = expr_node.value # 操作符: >, <, >=, <=, ==, !=
|
136
|
-
|
136
|
+
|
137
137
|
# 尝试类型转换
|
138
138
|
if isinstance(left_value, str) and str(left_value).isdigit():
|
139
139
|
left_value = int(left_value)
|
140
140
|
if isinstance(right_value, str) and str(right_value).isdigit():
|
141
141
|
right_value = int(right_value)
|
142
|
-
|
142
|
+
|
143
143
|
# 根据操作符执行相应的比较操作
|
144
144
|
if operator == '>':
|
145
145
|
return left_value > right_value
|
@@ -155,31 +155,31 @@ class DSLExecutor:
|
|
155
155
|
return left_value != right_value
|
156
156
|
else:
|
157
157
|
raise Exception(f"未知的比较操作符: {operator}")
|
158
|
-
|
158
|
+
|
159
159
|
def _eval_arithmetic_expr(self, expr_node):
|
160
160
|
"""
|
161
161
|
对算术表达式进行求值
|
162
|
-
|
162
|
+
|
163
163
|
:param expr_node: 算术表达式节点
|
164
164
|
:return: 计算结果
|
165
165
|
"""
|
166
166
|
left_value = self.eval_expression(expr_node.children[0])
|
167
167
|
right_value = self.eval_expression(expr_node.children[1])
|
168
168
|
operator = expr_node.value # 操作符: +, -, *, /
|
169
|
-
|
169
|
+
|
170
170
|
# 尝试类型转换 - 如果是字符串数字则转为数字
|
171
171
|
if isinstance(left_value, str) and str(left_value).replace('.', '', 1).isdigit():
|
172
172
|
left_value = float(left_value)
|
173
173
|
# 如果是整数则转为整数
|
174
174
|
if left_value.is_integer():
|
175
175
|
left_value = int(left_value)
|
176
|
-
|
176
|
+
|
177
177
|
if isinstance(right_value, str) and str(right_value).replace('.', '', 1).isdigit():
|
178
178
|
right_value = float(right_value)
|
179
179
|
# 如果是整数则转为整数
|
180
180
|
if right_value.is_integer():
|
181
181
|
right_value = int(right_value)
|
182
|
-
|
182
|
+
|
183
183
|
# 进行相应的算术运算
|
184
184
|
if operator == '+':
|
185
185
|
# 对于字符串,+是连接操作
|
@@ -202,18 +202,48 @@ class DSLExecutor:
|
|
202
202
|
return left_value / right_value
|
203
203
|
else:
|
204
204
|
raise Exception(f"未知的算术操作符: {operator}")
|
205
|
-
|
205
|
+
|
206
206
|
def _get_variable(self, var_name):
|
207
207
|
"""获取变量值,优先从本地变量获取,如果不存在则尝试从全局上下文获取"""
|
208
208
|
return self.variable_replacer.get_variable(var_name)
|
209
|
-
|
209
|
+
|
210
210
|
def _replace_variables_in_string(self, value):
|
211
211
|
"""替换字符串中的变量引用"""
|
212
212
|
return self.variable_replacer.replace_in_string(value)
|
213
|
-
|
213
|
+
|
214
|
+
def _handle_remote_import(self, node):
|
215
|
+
"""处理远程关键字导入
|
216
|
+
|
217
|
+
Args:
|
218
|
+
node: RemoteImport节点
|
219
|
+
"""
|
220
|
+
from pytest_dsl.remote.keyword_client import remote_keyword_manager
|
221
|
+
|
222
|
+
remote_info = node.value
|
223
|
+
url = self._replace_variables_in_string(remote_info['url'])
|
224
|
+
alias = remote_info['alias']
|
225
|
+
|
226
|
+
print(f"正在连接远程关键字服务器: {url}, 别名: {alias}")
|
227
|
+
|
228
|
+
# 注册远程服务器
|
229
|
+
success = remote_keyword_manager.register_remote_server(url, alias)
|
230
|
+
|
231
|
+
if not success:
|
232
|
+
print(f"无法连接到远程关键字服务器: {url}")
|
233
|
+
raise Exception(f"无法连接到远程关键字服务器: {url}")
|
234
|
+
|
235
|
+
print(f"已成功连接到远程关键字服务器: {url}, 别名: {alias}")
|
236
|
+
|
237
|
+
allure.attach(
|
238
|
+
f"已连接到远程关键字服务器: {url}\n"
|
239
|
+
f"别名: {alias}",
|
240
|
+
name="远程关键字导入",
|
241
|
+
attachment_type=allure.attachment_type.TEXT
|
242
|
+
)
|
243
|
+
|
214
244
|
def _handle_custom_keywords_in_file(self, node):
|
215
245
|
"""处理文件中的自定义关键字定义
|
216
|
-
|
246
|
+
|
217
247
|
Args:
|
218
248
|
node: Start节点
|
219
249
|
"""
|
@@ -233,7 +263,7 @@ class DSLExecutor:
|
|
233
263
|
self.test_context.clear()
|
234
264
|
metadata = {}
|
235
265
|
teardown_node = None
|
236
|
-
|
266
|
+
|
237
267
|
# 先处理元数据和找到teardown节点
|
238
268
|
for child in node.children:
|
239
269
|
if child.type == 'Metadata':
|
@@ -242,14 +272,17 @@ class DSLExecutor:
|
|
242
272
|
# 处理导入指令
|
243
273
|
if item.type == '@import':
|
244
274
|
self._handle_import(item.value)
|
275
|
+
# 处理远程关键字导入
|
276
|
+
elif item.type == 'RemoteImport':
|
277
|
+
self._handle_remote_import(item)
|
245
278
|
elif child.type == 'Teardown':
|
246
279
|
teardown_node = child
|
247
|
-
|
280
|
+
|
248
281
|
# 在_execute_test_iteration之前添加
|
249
282
|
self._handle_custom_keywords_in_file(node)
|
250
283
|
# 执行测试
|
251
284
|
self._execute_test_iteration(metadata, node, teardown_node)
|
252
|
-
|
285
|
+
|
253
286
|
except Exception as e:
|
254
287
|
# 如果是断言错误,直接抛出
|
255
288
|
if isinstance(e, AssertionError):
|
@@ -267,14 +300,14 @@ class DSLExecutor:
|
|
267
300
|
|
268
301
|
def _handle_import(self, file_path):
|
269
302
|
"""处理导入指令
|
270
|
-
|
303
|
+
|
271
304
|
Args:
|
272
305
|
file_path: 资源文件路径
|
273
306
|
"""
|
274
307
|
# 防止循环导入
|
275
308
|
if file_path in self.imported_files:
|
276
309
|
return
|
277
|
-
|
310
|
+
|
278
311
|
try:
|
279
312
|
# 导入自定义关键字文件
|
280
313
|
from pytest_dsl.core.custom_keyword_manager import custom_keyword_manager
|
@@ -297,12 +330,12 @@ class DSLExecutor:
|
|
297
330
|
if '@tags' in metadata:
|
298
331
|
for tag in metadata['@tags']:
|
299
332
|
allure.dynamic.tag(tag.value)
|
300
|
-
|
333
|
+
|
301
334
|
# 执行所有非teardown节点
|
302
335
|
for child in node.children:
|
303
336
|
if child.type != 'Teardown' and child.type != 'Metadata':
|
304
337
|
self.execute(child)
|
305
|
-
|
338
|
+
|
306
339
|
# 执行teardown
|
307
340
|
if teardown_node:
|
308
341
|
with allure.step("执行清理操作"):
|
@@ -320,7 +353,7 @@ class DSLExecutor:
|
|
320
353
|
# 否则清空变量(用于正常DSL执行)
|
321
354
|
import os
|
322
355
|
keep_variables = os.environ.get('PYTEST_DSL_KEEP_VARIABLES', '0') == '1'
|
323
|
-
|
356
|
+
|
324
357
|
if not keep_variables:
|
325
358
|
self.variables.clear()
|
326
359
|
# 同时清空测试上下文
|
@@ -336,7 +369,7 @@ class DSLExecutor:
|
|
336
369
|
"""处理赋值语句"""
|
337
370
|
var_name = node.value
|
338
371
|
expr_value = self.eval_expression(node.children[0])
|
339
|
-
|
372
|
+
|
340
373
|
# 检查变量名是否以g_开头,如果是则设置为全局变量
|
341
374
|
if var_name.startswith('g_'):
|
342
375
|
global_context.set_variable(var_name, expr_value)
|
@@ -361,22 +394,42 @@ class DSLExecutor:
|
|
361
394
|
var_name = node.value
|
362
395
|
keyword_call_node = node.children[0]
|
363
396
|
result = self.execute(keyword_call_node)
|
364
|
-
|
397
|
+
|
365
398
|
if result is not None:
|
399
|
+
# 处理新的统一返回格式(支持远程关键字模式)
|
400
|
+
if isinstance(result, dict) and 'result' in result:
|
401
|
+
# 提取主要返回值
|
402
|
+
main_result = result['result']
|
403
|
+
|
404
|
+
# 处理captures字段中的变量
|
405
|
+
captures = result.get('captures', {})
|
406
|
+
for capture_var, capture_value in captures.items():
|
407
|
+
if capture_var.startswith('g_'):
|
408
|
+
global_context.set_variable(capture_var, capture_value)
|
409
|
+
else:
|
410
|
+
self.variable_replacer.local_variables[capture_var] = capture_value
|
411
|
+
self.test_context.set(capture_var, capture_value)
|
412
|
+
|
413
|
+
# 将主要结果赋值给指定变量
|
414
|
+
actual_result = main_result
|
415
|
+
else:
|
416
|
+
# 传统格式,直接使用结果
|
417
|
+
actual_result = result
|
418
|
+
|
366
419
|
# 检查变量名是否以g_开头,如果是则设置为全局变量
|
367
420
|
if var_name.startswith('g_'):
|
368
|
-
global_context.set_variable(var_name,
|
421
|
+
global_context.set_variable(var_name, actual_result)
|
369
422
|
allure.attach(
|
370
|
-
f"全局变量: {var_name}\n值: {
|
423
|
+
f"全局变量: {var_name}\n值: {actual_result}",
|
371
424
|
name="全局变量赋值",
|
372
425
|
attachment_type=allure.attachment_type.TEXT
|
373
426
|
)
|
374
427
|
else:
|
375
428
|
# 存储在本地变量字典和测试上下文中
|
376
|
-
self.variable_replacer.local_variables[var_name] =
|
377
|
-
self.test_context.set(var_name,
|
429
|
+
self.variable_replacer.local_variables[var_name] = actual_result
|
430
|
+
self.test_context.set(var_name, actual_result) # 同时添加到测试上下文
|
378
431
|
allure.attach(
|
379
|
-
f"变量: {var_name}\n值: {
|
432
|
+
f"变量: {var_name}\n值: {actual_result}",
|
380
433
|
name="赋值详情",
|
381
434
|
attachment_type=allure.attachment_type.TEXT
|
382
435
|
)
|
@@ -389,7 +442,7 @@ class DSLExecutor:
|
|
389
442
|
var_name = node.value
|
390
443
|
start = self.eval_expression(node.children[0])
|
391
444
|
end = self.eval_expression(node.children[1])
|
392
|
-
|
445
|
+
|
393
446
|
for i in range(int(start), int(end)):
|
394
447
|
# 存储在本地变量字典和测试上下文中
|
395
448
|
self.variable_replacer.local_variables[var_name] = i
|
@@ -403,9 +456,9 @@ class DSLExecutor:
|
|
403
456
|
keyword_info = keyword_manager.get_keyword_info(keyword_name)
|
404
457
|
if not keyword_info:
|
405
458
|
raise Exception(f"未注册的关键字: {keyword_name}")
|
406
|
-
|
459
|
+
|
407
460
|
kwargs = self._prepare_keyword_params(node, keyword_info)
|
408
|
-
|
461
|
+
|
409
462
|
try:
|
410
463
|
# 由于KeywordManager中的wrapper已经添加了allure.step和日志,这里不再重复添加
|
411
464
|
result = keyword_manager.execute(keyword_name, **kwargs)
|
@@ -418,7 +471,7 @@ class DSLExecutor:
|
|
418
471
|
"""准备关键字调用参数"""
|
419
472
|
mapping = keyword_info.get('mapping', {})
|
420
473
|
kwargs = {'context': self.test_context} # 默认传入context参数
|
421
|
-
|
474
|
+
|
422
475
|
# 检查是否有参数列表
|
423
476
|
if node.children[0]:
|
424
477
|
for param in node.children[0]:
|
@@ -427,7 +480,7 @@ class DSLExecutor:
|
|
427
480
|
# 对参数值进行变量替换
|
428
481
|
param_value = self.eval_expression(param.children[0])
|
429
482
|
kwargs[english_param_name] = param_value
|
430
|
-
|
483
|
+
|
431
484
|
return kwargs
|
432
485
|
|
433
486
|
@allure.step("执行清理操作")
|
@@ -438,10 +491,10 @@ class DSLExecutor:
|
|
438
491
|
@allure.step("执行返回语句")
|
439
492
|
def _handle_return(self, node):
|
440
493
|
"""处理return语句
|
441
|
-
|
494
|
+
|
442
495
|
Args:
|
443
496
|
node: Return节点
|
444
|
-
|
497
|
+
|
445
498
|
Returns:
|
446
499
|
表达式求值结果
|
447
500
|
"""
|
@@ -451,12 +504,12 @@ class DSLExecutor:
|
|
451
504
|
@allure.step("执行条件语句")
|
452
505
|
def _handle_if_statement(self, node):
|
453
506
|
"""处理if-else语句
|
454
|
-
|
507
|
+
|
455
508
|
Args:
|
456
509
|
node: IfStatement节点,包含条件表达式、if分支和可选的else分支
|
457
510
|
"""
|
458
511
|
condition = self.eval_expression(node.children[0])
|
459
|
-
|
512
|
+
|
460
513
|
# 将条件转换为布尔值进行评估
|
461
514
|
if condition:
|
462
515
|
# 执行if分支
|
@@ -466,10 +519,112 @@ class DSLExecutor:
|
|
466
519
|
# 如果存在else分支且条件为假,则执行else分支
|
467
520
|
with allure.step("执行else分支"):
|
468
521
|
return self.execute(node.children[2])
|
469
|
-
|
522
|
+
|
470
523
|
# 如果条件为假且没有else分支,则不执行任何操作
|
471
524
|
return None
|
472
525
|
|
526
|
+
def _execute_remote_keyword_call(self, node):
|
527
|
+
"""执行远程关键字调用
|
528
|
+
|
529
|
+
Args:
|
530
|
+
node: RemoteKeywordCall节点
|
531
|
+
|
532
|
+
Returns:
|
533
|
+
执行结果
|
534
|
+
"""
|
535
|
+
from pytest_dsl.remote.keyword_client import remote_keyword_manager
|
536
|
+
|
537
|
+
call_info = node.value
|
538
|
+
alias = call_info['alias']
|
539
|
+
keyword_name = call_info['keyword']
|
540
|
+
|
541
|
+
# 准备参数
|
542
|
+
params = []
|
543
|
+
if node.children and node.children[0]:
|
544
|
+
params = node.children[0]
|
545
|
+
|
546
|
+
kwargs = {}
|
547
|
+
for param in params:
|
548
|
+
param_name = param.value
|
549
|
+
param_value = self.eval_expression(param.children[0])
|
550
|
+
kwargs[param_name] = param_value
|
551
|
+
|
552
|
+
# 添加测试上下文
|
553
|
+
kwargs['context'] = self.test_context
|
554
|
+
|
555
|
+
with allure.step(f"执行远程关键字: {alias}|{keyword_name}"):
|
556
|
+
try:
|
557
|
+
# 执行远程关键字
|
558
|
+
result = remote_keyword_manager.execute_remote_keyword(alias, keyword_name, **kwargs)
|
559
|
+
allure.attach(
|
560
|
+
f"远程关键字参数: {kwargs}\n"
|
561
|
+
f"远程关键字结果: {result}",
|
562
|
+
name="远程关键字执行详情",
|
563
|
+
attachment_type=allure.attachment_type.TEXT
|
564
|
+
)
|
565
|
+
return result
|
566
|
+
except Exception as e:
|
567
|
+
# 记录错误并重新抛出
|
568
|
+
allure.attach(
|
569
|
+
f"远程关键字执行失败: {str(e)}",
|
570
|
+
name="远程关键字错误",
|
571
|
+
attachment_type=allure.attachment_type.TEXT
|
572
|
+
)
|
573
|
+
raise
|
574
|
+
|
575
|
+
def _handle_assignment_remote_keyword_call(self, node):
|
576
|
+
"""处理远程关键字调用赋值
|
577
|
+
|
578
|
+
Args:
|
579
|
+
node: AssignmentRemoteKeywordCall节点
|
580
|
+
"""
|
581
|
+
var_name = node.value
|
582
|
+
remote_keyword_call_node = node.children[0]
|
583
|
+
result = self.execute(remote_keyword_call_node)
|
584
|
+
|
585
|
+
if result is not None:
|
586
|
+
# 注意:远程关键字客户端已经处理了新格式的返回值,
|
587
|
+
# 这里接收到的result应该已经是主要返回值,而不是完整的字典格式
|
588
|
+
# 但为了保险起见,我们仍然检查是否为新格式
|
589
|
+
if isinstance(result, dict) and 'result' in result:
|
590
|
+
# 如果仍然是新格式(可能是嵌套的远程调用),提取主要返回值
|
591
|
+
main_result = result['result']
|
592
|
+
|
593
|
+
# 处理captures字段中的变量
|
594
|
+
captures = result.get('captures', {})
|
595
|
+
for capture_var, capture_value in captures.items():
|
596
|
+
if capture_var.startswith('g_'):
|
597
|
+
global_context.set_variable(capture_var, capture_value)
|
598
|
+
else:
|
599
|
+
self.variable_replacer.local_variables[capture_var] = capture_value
|
600
|
+
self.test_context.set(capture_var, capture_value)
|
601
|
+
|
602
|
+
# 将主要结果赋值给指定变量
|
603
|
+
actual_result = main_result
|
604
|
+
else:
|
605
|
+
# 传统格式或已经处理过的格式,直接使用结果
|
606
|
+
actual_result = result
|
607
|
+
|
608
|
+
# 检查变量名是否以g_开头,如果是则设置为全局变量
|
609
|
+
if var_name.startswith('g_'):
|
610
|
+
global_context.set_variable(var_name, actual_result)
|
611
|
+
allure.attach(
|
612
|
+
f"全局变量: {var_name}\n值: {actual_result}",
|
613
|
+
name="全局变量赋值",
|
614
|
+
attachment_type=allure.attachment_type.TEXT
|
615
|
+
)
|
616
|
+
else:
|
617
|
+
# 存储在本地变量字典和测试上下文中
|
618
|
+
self.variable_replacer.local_variables[var_name] = actual_result
|
619
|
+
self.test_context.set(var_name, actual_result) # 同时添加到测试上下文
|
620
|
+
allure.attach(
|
621
|
+
f"变量: {var_name}\n值: {actual_result}",
|
622
|
+
name="赋值详情",
|
623
|
+
attachment_type=allure.attachment_type.TEXT
|
624
|
+
)
|
625
|
+
else:
|
626
|
+
raise Exception(f"远程关键字没有返回结果")
|
627
|
+
|
473
628
|
def execute(self, node):
|
474
629
|
"""执行AST节点"""
|
475
630
|
handlers = {
|
@@ -483,9 +638,12 @@ class DSLExecutor:
|
|
483
638
|
'Teardown': self._handle_teardown,
|
484
639
|
'Return': self._handle_return,
|
485
640
|
'IfStatement': self._handle_if_statement,
|
486
|
-
'CustomKeyword': lambda _: None # 添加对CustomKeyword节点的处理,只需注册不需执行
|
641
|
+
'CustomKeyword': lambda _: None, # 添加对CustomKeyword节点的处理,只需注册不需执行
|
642
|
+
'RemoteImport': self._handle_remote_import,
|
643
|
+
'RemoteKeywordCall': self._execute_remote_keyword_call,
|
644
|
+
'AssignmentRemoteKeywordCall': self._handle_assignment_remote_keyword_call
|
487
645
|
}
|
488
|
-
|
646
|
+
|
489
647
|
handler = handlers.get(node.type)
|
490
648
|
if handler:
|
491
649
|
return handler(node)
|
@@ -494,4 +652,4 @@ class DSLExecutor:
|
|
494
652
|
def read_file(filename):
|
495
653
|
"""读取 DSL 文件内容"""
|
496
654
|
with open(filename, 'r', encoding='utf-8') as f:
|
497
|
-
return f.read()
|
655
|
+
return f.read()
|