pytest-dsl 0.15.3__py3-none-any.whl → 0.15.4__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/__init__.py +52 -3
- pytest_dsl/cli.py +8 -1
- pytest_dsl/core/context.py +59 -5
- pytest_dsl/core/custom_keyword_manager.py +14 -8
- pytest_dsl/core/dsl_executor.py +28 -8
- pytest_dsl/core/global_context.py +38 -8
- pytest_dsl/core/http_client.py +29 -5
- pytest_dsl/core/keyword_utils.py +7 -0
- pytest_dsl/core/remote_server_registry.py +333 -0
- pytest_dsl/core/utils.py +34 -27
- pytest_dsl/core/variable_providers.py +202 -0
- pytest_dsl/core/variable_utils.py +45 -38
- pytest_dsl/core/yaml_loader.py +176 -36
- pytest_dsl/keywords/http_keywords.py +9 -5
- pytest_dsl/remote/__init__.py +63 -1
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/METADATA +1 -1
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/RECORD +21 -19
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/WHEEL +0 -0
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.15.3.dist-info → pytest_dsl-0.15.4.dist-info}/top_level.txt +0 -0
@@ -1,50 +1,54 @@
|
|
1
|
+
"""变量替换工具模块
|
2
|
+
|
3
|
+
该模块提供了高级的变量替换功能,支持复杂的变量访问语法。
|
4
|
+
"""
|
5
|
+
|
1
6
|
import re
|
2
7
|
import json
|
3
|
-
from typing import Any, Dict, List
|
8
|
+
from typing import Any, Dict, List
|
4
9
|
from pytest_dsl.core.global_context import global_context
|
5
10
|
from pytest_dsl.core.context import TestContext
|
6
|
-
from pytest_dsl.core.yaml_vars import yaml_vars
|
7
11
|
|
8
12
|
|
9
13
|
class VariableReplacer:
|
10
|
-
"""
|
11
|
-
|
12
|
-
提供统一的变量替换功能,支持字符串、字典和列表中的变量替换。
|
13
|
-
变量查找优先级:本地变量 > 测试上下文 > YAML变量 > 全局上下文
|
14
|
-
"""
|
14
|
+
"""变量替换器,支持高级变量访问语法"""
|
15
15
|
|
16
16
|
def __init__(self, local_variables: Dict[str, Any] = None, test_context: TestContext = None):
|
17
17
|
"""初始化变量替换器
|
18
18
|
|
19
19
|
Args:
|
20
20
|
local_variables: 本地变量字典
|
21
|
-
test_context:
|
21
|
+
test_context: 测试上下文
|
22
22
|
"""
|
23
23
|
self.local_variables = local_variables or {}
|
24
|
-
self._test_context = test_context
|
24
|
+
self._test_context = test_context
|
25
25
|
|
26
26
|
@property
|
27
27
|
def test_context(self) -> TestContext:
|
28
|
-
"""
|
28
|
+
"""获取测试上下文,如果没有提供则尝试从关键字管理器获取"""
|
29
|
+
if self._test_context:
|
30
|
+
return self._test_context
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
if hasattr(self._test_context, 'executor') and self._test_context.executor is not None:
|
37
|
-
return self._test_context.executor.test_context
|
38
|
-
return self._test_context
|
32
|
+
# 尝试从关键字管理器获取当前上下文
|
33
|
+
try:
|
34
|
+
from pytest_dsl.core.keyword_manager import keyword_manager
|
35
|
+
return getattr(keyword_manager, 'current_context', None)
|
36
|
+
except ImportError:
|
37
|
+
return None
|
39
38
|
|
40
39
|
def get_variable(self, var_name: str) -> Any:
|
41
|
-
"""
|
40
|
+
"""获取变量值,按优先级顺序查找
|
41
|
+
|
42
|
+
查找顺序:
|
43
|
+
1. 本地变量
|
44
|
+
2. 测试上下文
|
45
|
+
3. 全局上下文(包含YAML变量的访问)
|
42
46
|
|
43
47
|
Args:
|
44
48
|
var_name: 变量名
|
45
49
|
|
46
50
|
Returns:
|
47
|
-
|
51
|
+
变量值
|
48
52
|
|
49
53
|
Raises:
|
50
54
|
KeyError: 当变量不存在时
|
@@ -54,17 +58,13 @@ class VariableReplacer:
|
|
54
58
|
value = self.local_variables[var_name]
|
55
59
|
return self._convert_value(value)
|
56
60
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
61
|
+
# 从测试上下文中获取(优先级高于YAML变量)
|
62
|
+
context = self.test_context
|
63
|
+
if context and context.has(var_name):
|
64
|
+
value = context.get(var_name)
|
60
65
|
return self._convert_value(value)
|
61
66
|
|
62
|
-
#
|
63
|
-
yaml_value = yaml_vars.get_variable(var_name)
|
64
|
-
if yaml_value is not None:
|
65
|
-
return self._convert_value(yaml_value)
|
66
|
-
|
67
|
-
# 从全局上下文获取
|
67
|
+
# 从全局上下文获取(包含对YAML变量的统一访问)
|
68
68
|
if global_context.has_variable(var_name):
|
69
69
|
value = global_context.get_variable(var_name)
|
70
70
|
return self._convert_value(value)
|
@@ -130,7 +130,8 @@ class VariableReplacer:
|
|
130
130
|
try:
|
131
131
|
var_value = self._parse_variable_path(var_ref)
|
132
132
|
# 替换变量引用
|
133
|
-
result = result[:match.start()] + str(var_value) +
|
133
|
+
result = result[:match.start()] + str(var_value) + \
|
134
|
+
result[match.end():]
|
134
135
|
except (KeyError, IndexError, TypeError) as e:
|
135
136
|
raise KeyError(f"无法解析变量引用 '${{{var_ref}}}': {str(e)}")
|
136
137
|
|
@@ -170,7 +171,8 @@ class VariableReplacer:
|
|
170
171
|
|
171
172
|
# 逐步访问路径
|
172
173
|
for part in path_parts[1:]:
|
173
|
-
current_value = self._access_value(
|
174
|
+
current_value = self._access_value(
|
175
|
+
current_value, part, root_var_name)
|
174
176
|
|
175
177
|
return current_value
|
176
178
|
|
@@ -258,14 +260,16 @@ class VariableReplacer:
|
|
258
260
|
raise KeyError(f"字典中不存在键 '{key}'")
|
259
261
|
return current_value[key]
|
260
262
|
else:
|
261
|
-
raise TypeError(
|
263
|
+
raise TypeError(
|
264
|
+
f"无法在 {type(current_value).__name__} 类型上使用字符串键访问")
|
262
265
|
|
263
266
|
# 处理数字索引
|
264
267
|
try:
|
265
268
|
index = int(key_content)
|
266
269
|
if isinstance(current_value, (list, tuple)):
|
267
270
|
if index >= len(current_value) or index < -len(current_value):
|
268
|
-
raise IndexError(
|
271
|
+
raise IndexError(
|
272
|
+
f"数组索引 {index} 超出范围,数组长度为 {len(current_value)}")
|
269
273
|
return current_value[index]
|
270
274
|
elif isinstance(current_value, dict):
|
271
275
|
# 字典也可以用数字键
|
@@ -274,7 +278,8 @@ class VariableReplacer:
|
|
274
278
|
raise KeyError(f"字典中不存在键 '{index}' 或 '{str_key}'")
|
275
279
|
return current_value.get(index, current_value.get(str_key))
|
276
280
|
else:
|
277
|
-
raise TypeError(
|
281
|
+
raise TypeError(
|
282
|
+
f"无法在 {type(current_value).__name__} 类型上使用索引访问")
|
278
283
|
except ValueError:
|
279
284
|
raise ValueError(f"无效的索引格式: '{key_content}'")
|
280
285
|
|
@@ -283,7 +288,8 @@ class VariableReplacer:
|
|
283
288
|
if isinstance(current_value, dict) and access_token in current_value:
|
284
289
|
return current_value[access_token]
|
285
290
|
else:
|
286
|
-
raise KeyError(
|
291
|
+
raise KeyError(
|
292
|
+
f"无法访问属性 '{access_token}',当前值类型是 {type(current_value).__name__}")
|
287
293
|
|
288
294
|
def replace_in_dict(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
289
295
|
"""递归替换字典中的变量引用
|
@@ -303,7 +309,8 @@ class VariableReplacer:
|
|
303
309
|
result = {}
|
304
310
|
for key, value in data.items():
|
305
311
|
# 替换键中的变量
|
306
|
-
new_key = self.replace_in_string(
|
312
|
+
new_key = self.replace_in_string(
|
313
|
+
key) if isinstance(key, str) else key
|
307
314
|
# 替换值中的变量
|
308
315
|
new_value = self.replace_in_value(value)
|
309
316
|
result[new_key] = new_value
|
@@ -409,4 +416,4 @@ class VariableReplacer:
|
|
409
416
|
return yaml.dump(replaced_data, allow_unicode=True)
|
410
417
|
except:
|
411
418
|
# 如果YAML解析失败,直接作为字符串处理
|
412
|
-
return self.replace_in_string(yaml_str)
|
419
|
+
return self.replace_in_string(yaml_str)
|
pytest_dsl/core/yaml_loader.py
CHANGED
@@ -31,50 +31,91 @@ def add_yaml_options(parser):
|
|
31
31
|
|
32
32
|
|
33
33
|
def load_yaml_variables_from_args(yaml_files=None, yaml_vars_dir=None,
|
34
|
-
project_root=None, environment=None
|
35
|
-
|
34
|
+
project_root=None, environment=None,
|
35
|
+
auto_load_default=None):
|
36
|
+
"""从命令行参数加载YAML变量
|
36
37
|
|
37
38
|
Args:
|
38
39
|
yaml_files: YAML文件列表
|
39
40
|
yaml_vars_dir: YAML变量目录路径
|
40
|
-
project_root:
|
41
|
+
project_root: 项目根目录
|
41
42
|
environment: 环境名称(用于hook加载)
|
43
|
+
auto_load_default: 是否自动加载默认配置
|
44
|
+
None - 根据用户输入智能判断
|
45
|
+
True - 强制加载默认配置
|
46
|
+
False - 不加载默认配置
|
42
47
|
"""
|
43
|
-
#
|
48
|
+
# 智能判断是否应该加载默认配置
|
49
|
+
if auto_load_default is None:
|
50
|
+
# 如果用户指定了具体的YAML文件,不自动加载默认配置
|
51
|
+
# 如果用户指定了具体的目录,也不自动加载默认配置
|
52
|
+
user_specified_files = bool(yaml_files)
|
53
|
+
user_specified_dir = bool(yaml_vars_dir)
|
54
|
+
auto_load_default = not (user_specified_files or user_specified_dir)
|
55
|
+
|
56
|
+
if not auto_load_default:
|
57
|
+
print("🎯 检测到用户指定了配置,跳过默认配置自动加载")
|
58
|
+
else:
|
59
|
+
print("📁 未指定配置,将自动加载默认配置目录")
|
60
|
+
|
61
|
+
# 首先尝试通过hook加载变量(最高优先级)
|
44
62
|
hook_variables = _load_variables_through_hooks(
|
45
63
|
project_root=project_root, environment=environment)
|
46
64
|
|
47
65
|
if hook_variables:
|
48
|
-
print(f"通过Hook加载了 {len(hook_variables)} 个变量")
|
66
|
+
print(f"🔌 通过Hook加载了 {len(hook_variables)} 个变量")
|
49
67
|
# 将hook变量加载到yaml_vars中
|
50
68
|
yaml_vars._variables.update(hook_variables)
|
51
69
|
|
52
|
-
#
|
70
|
+
# 加载用户指定的YAML文件(第二优先级)
|
53
71
|
if yaml_files:
|
54
72
|
yaml_vars.load_yaml_files(yaml_files)
|
55
|
-
print(f"
|
56
|
-
|
57
|
-
#
|
58
|
-
if yaml_vars_dir
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
print(f"已加载YAML变量目录: {yaml_vars_dir}")
|
66
|
-
loaded_files = yaml_vars.get_loaded_files()
|
67
|
-
if loaded_files:
|
68
|
-
# 过滤出当前目录的文件
|
69
|
-
if yaml_vars_dir:
|
73
|
+
print(f"📄 已加载用户指定的YAML文件: {', '.join(yaml_files)}")
|
74
|
+
|
75
|
+
# 加载用户指定的目录中的YAML文件(第三优先级)
|
76
|
+
if yaml_vars_dir:
|
77
|
+
if Path(yaml_vars_dir).exists():
|
78
|
+
yaml_vars.load_from_directory(yaml_vars_dir)
|
79
|
+
print(f"📂 已加载用户指定的YAML目录: {yaml_vars_dir}")
|
80
|
+
loaded_files = yaml_vars.get_loaded_files()
|
81
|
+
if loaded_files:
|
82
|
+
# 过滤出当前目录的文件
|
70
83
|
dir_files = [f for f in loaded_files if Path(
|
71
84
|
f).parent == Path(yaml_vars_dir)]
|
72
85
|
if dir_files:
|
73
|
-
print(f"目录中加载的文件: {', '.join(dir_files)}")
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
86
|
+
print(f" 目录中加载的文件: {', '.join(dir_files)}")
|
87
|
+
else:
|
88
|
+
print(f"⚠️ 用户指定的YAML目录不存在: {yaml_vars_dir}")
|
89
|
+
|
90
|
+
# 自动加载默认配置(最低优先级,仅在用户未指定配置时)
|
91
|
+
if auto_load_default:
|
92
|
+
default_yaml_vars_dir = None
|
93
|
+
if project_root:
|
94
|
+
# 默认使用项目根目录下的config目录
|
95
|
+
default_yaml_vars_dir = str(Path(project_root) / 'config')
|
96
|
+
print(f"🏠 使用默认YAML变量目录: {default_yaml_vars_dir}")
|
97
|
+
|
98
|
+
if default_yaml_vars_dir and Path(default_yaml_vars_dir).exists():
|
99
|
+
yaml_vars.load_from_directory(default_yaml_vars_dir)
|
100
|
+
print(f"📁 已加载默认YAML变量目录: {default_yaml_vars_dir}")
|
101
|
+
loaded_files = yaml_vars.get_loaded_files()
|
102
|
+
if loaded_files:
|
103
|
+
# 过滤出默认目录的文件
|
104
|
+
dir_files = [f for f in loaded_files if Path(
|
105
|
+
f).parent == Path(default_yaml_vars_dir)]
|
106
|
+
if dir_files:
|
107
|
+
print(f" 默认目录中加载的文件: {', '.join(dir_files)}")
|
108
|
+
elif default_yaml_vars_dir:
|
109
|
+
print(f"📁 默认YAML变量目录不存在: {default_yaml_vars_dir}")
|
110
|
+
|
111
|
+
# 显示最终加载的变量汇总
|
112
|
+
all_loaded_files = yaml_vars.get_loaded_files()
|
113
|
+
if all_loaded_files:
|
114
|
+
print(f"✅ 变量加载完成,共加载 {len(all_loaded_files)} 个文件")
|
115
|
+
if len(yaml_vars._variables) > 0:
|
116
|
+
print(f"📊 总共加载了 {len(yaml_vars._variables)} 个变量")
|
117
|
+
else:
|
118
|
+
print("⚠️ 未加载任何YAML变量文件")
|
78
119
|
|
79
120
|
# 加载完YAML变量后,自动连接远程服务器
|
80
121
|
load_remote_servers_from_yaml()
|
@@ -203,37 +244,136 @@ def load_yaml_variables(config):
|
|
203
244
|
)
|
204
245
|
|
205
246
|
|
206
|
-
def load_remote_servers_from_yaml():
|
207
|
-
"""从YAML变量中加载远程服务器配置
|
247
|
+
def load_remote_servers_from_yaml(variable_source=None):
|
248
|
+
"""从YAML变量中加载远程服务器配置
|
249
|
+
|
250
|
+
Args:
|
251
|
+
variable_source: 自定义变量源,如果不提供则使用全局上下文
|
252
|
+
"""
|
208
253
|
try:
|
209
254
|
from pytest_dsl.remote.keyword_client import remote_keyword_manager
|
210
255
|
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
|
256
|
+
# 支持自定义变量源
|
257
|
+
if variable_source and callable(variable_source):
|
258
|
+
variables = variable_source()
|
259
|
+
remote_servers = variables.get(
|
260
|
+
'remote_servers') if isinstance(variables, dict) else None
|
261
|
+
else:
|
262
|
+
# 使用默认的全局上下文
|
263
|
+
from pytest_dsl.core.global_context import global_context
|
264
|
+
remote_servers = global_context.get_variable('remote_servers')
|
215
265
|
|
216
|
-
|
266
|
+
if not remote_servers:
|
267
|
+
return []
|
268
|
+
|
269
|
+
# 支持两种格式:数组和字典
|
270
|
+
server_configs = []
|
271
|
+
|
272
|
+
if isinstance(remote_servers, list):
|
273
|
+
# 数组格式: [{'url': '...', 'alias': '...'}, ...]
|
274
|
+
server_configs = remote_servers
|
275
|
+
print(f"发现 {len(remote_servers)} 个远程服务器配置(数组格式)")
|
276
|
+
elif isinstance(remote_servers, dict):
|
277
|
+
# 字典格式: {'server1': {'url': '...', 'alias': '...'}, ...}
|
278
|
+
for server_name, server_config in remote_servers.items():
|
279
|
+
if isinstance(server_config, dict):
|
280
|
+
# 如果没有指定alias,使用键名作为alias
|
281
|
+
if 'alias' not in server_config:
|
282
|
+
server_config = server_config.copy()
|
283
|
+
server_config['alias'] = server_name
|
284
|
+
server_configs.append(server_config)
|
285
|
+
print(f"发现 {len(server_configs)} 个远程服务器配置(字典格式)")
|
286
|
+
else:
|
287
|
+
print(
|
288
|
+
f"警告:remote_servers配置格式不支持,期望数组或字典,得到 {type(remote_servers)}")
|
289
|
+
return []
|
290
|
+
|
291
|
+
results = []
|
217
292
|
|
218
293
|
# 注册远程服务器
|
219
|
-
for server_config in
|
294
|
+
for server_config in server_configs:
|
220
295
|
if isinstance(server_config, dict):
|
221
296
|
url = server_config.get('url')
|
222
297
|
alias = server_config.get('alias')
|
223
298
|
api_key = server_config.get('api_key')
|
299
|
+
sync_config = server_config.get('sync_config')
|
224
300
|
|
225
301
|
if url and alias:
|
226
302
|
print(f"自动连接远程服务器: {alias} -> {url}")
|
227
303
|
success = remote_keyword_manager.register_remote_server(
|
228
|
-
url, alias, api_key=api_key
|
304
|
+
url, alias, api_key=api_key, sync_config=sync_config
|
229
305
|
)
|
230
306
|
if success:
|
231
307
|
print(f"✓ 远程服务器 {alias} 连接成功")
|
232
308
|
else:
|
233
309
|
print(f"✗ 远程服务器 {alias} 连接失败")
|
234
310
|
|
311
|
+
results.append(
|
312
|
+
{'alias': alias, 'url': url, 'success': success})
|
313
|
+
else:
|
314
|
+
print(f"警告:服务器配置缺少必要字段 url 或 alias: {server_config}")
|
315
|
+
|
316
|
+
return results
|
317
|
+
|
235
318
|
except ImportError:
|
236
319
|
# 如果远程功能不可用,跳过
|
237
|
-
|
320
|
+
return []
|
238
321
|
except Exception as e:
|
239
322
|
print(f"自动连接远程服务器时出现警告: {e}")
|
323
|
+
return []
|
324
|
+
|
325
|
+
|
326
|
+
def register_remote_servers_from_config(servers_config, variable_providers=None):
|
327
|
+
"""从配置注册远程服务器的独立函数
|
328
|
+
|
329
|
+
这个函数可以被其他系统独立调用,不依赖YAML配置。
|
330
|
+
|
331
|
+
Args:
|
332
|
+
servers_config: 服务器配置列表或单个配置
|
333
|
+
variable_providers: 变量提供者列表,用于同步自定义变量
|
334
|
+
|
335
|
+
Returns:
|
336
|
+
dict: 注册结果
|
337
|
+
|
338
|
+
Examples:
|
339
|
+
>>> # 单个服务器
|
340
|
+
>>> config = {'url': 'http://server:8270', 'alias': 'test'}
|
341
|
+
>>> result = register_remote_servers_from_config(config)
|
342
|
+
|
343
|
+
>>> # 多个服务器
|
344
|
+
>>> configs = [
|
345
|
+
... {'url': 'http://server1:8270', 'alias': 'server1'},
|
346
|
+
... {'url': 'http://server2:8270', 'alias': 'server2'}
|
347
|
+
... ]
|
348
|
+
>>> results = register_remote_servers_from_config(configs)
|
349
|
+
|
350
|
+
>>> # 带变量提供者
|
351
|
+
>>> def my_vars():
|
352
|
+
... return {'env': 'prod', 'api_key': 'secret'}
|
353
|
+
>>> results = register_remote_servers_from_config(configs, [my_vars])
|
354
|
+
"""
|
355
|
+
try:
|
356
|
+
from pytest_dsl.core.remote_server_registry import remote_server_registry
|
357
|
+
|
358
|
+
# 如果有变量提供者,先添加它们
|
359
|
+
if variable_providers:
|
360
|
+
remote_server_registry.clear_variable_providers()
|
361
|
+
for provider in variable_providers:
|
362
|
+
if callable(provider):
|
363
|
+
remote_server_registry.add_variable_provider(provider)
|
364
|
+
|
365
|
+
# 确保servers_config是列表
|
366
|
+
if isinstance(servers_config, dict):
|
367
|
+
servers_config = [servers_config]
|
368
|
+
elif not isinstance(servers_config, list):
|
369
|
+
raise ValueError("servers_config必须是字典或字典列表")
|
370
|
+
|
371
|
+
# 使用注册器批量注册
|
372
|
+
return remote_server_registry.register_servers_from_config(servers_config)
|
373
|
+
|
374
|
+
except ImportError:
|
375
|
+
print("远程功能不可用,请检查依赖安装")
|
376
|
+
return {}
|
377
|
+
except Exception as e:
|
378
|
+
print(f"注册远程服务器时发生错误: {e}")
|
379
|
+
return {}
|
@@ -14,7 +14,6 @@ from typing import Dict, Any, Union
|
|
14
14
|
|
15
15
|
from pytest_dsl.core.keyword_manager import keyword_manager
|
16
16
|
from pytest_dsl.core.http_request import HTTPRequest
|
17
|
-
from pytest_dsl.core.yaml_vars import yaml_vars
|
18
17
|
from pytest_dsl.core.context import TestContext
|
19
18
|
|
20
19
|
# 配置日志
|
@@ -293,8 +292,8 @@ def http_request(context, **kwargs):
|
|
293
292
|
# 添加调试信息,检查客户端配置是否可用
|
294
293
|
print(f"🌐 HTTP请求 - 客户端: {client_name}")
|
295
294
|
|
296
|
-
#
|
297
|
-
http_clients_config =
|
295
|
+
# 从context获取http_clients配置(统一的变量获取方式)
|
296
|
+
http_clients_config = context.get("http_clients")
|
298
297
|
if http_clients_config:
|
299
298
|
print(f"✓ 找到http_clients配置,包含 {len(http_clients_config)} 个客户端")
|
300
299
|
if client_name in http_clients_config:
|
@@ -310,10 +309,15 @@ def http_request(context, **kwargs):
|
|
310
309
|
|
311
310
|
with allure.step(f"发送HTTP请求 (客户端: {client_name}"
|
312
311
|
f"{', 会话: ' + session_name if session_name else ''})"):
|
312
|
+
|
313
|
+
# 确保http_client_manager有正确的context引用
|
314
|
+
from pytest_dsl.core.http_client import http_client_manager
|
315
|
+
http_client_manager.set_context(context)
|
316
|
+
|
313
317
|
# 处理模板
|
314
318
|
if template_name:
|
315
|
-
# 从
|
316
|
-
http_templates =
|
319
|
+
# 从context获取模板配置(统一的变量获取方式)
|
320
|
+
http_templates = context.get("http_templates") or {}
|
317
321
|
template = http_templates.get(template_name)
|
318
322
|
|
319
323
|
if not template:
|
pytest_dsl/remote/__init__.py
CHANGED
@@ -4,4 +4,66 @@ Remote module for pytest-dsl.
|
|
4
4
|
This module provides remote keyword server functionality.
|
5
5
|
"""
|
6
6
|
|
7
|
-
__version__ = "0.
|
7
|
+
__version__ = "0.15.4"
|
8
|
+
|
9
|
+
# 导出远程关键字管理器和相关功能
|
10
|
+
from .keyword_client import remote_keyword_manager, RemoteKeywordManager, RemoteKeywordClient
|
11
|
+
from .keyword_server import RemoteKeywordServer
|
12
|
+
from .variable_bridge import VariableBridge
|
13
|
+
|
14
|
+
# 导出便捷函数
|
15
|
+
|
16
|
+
|
17
|
+
def register_remote_server(url, alias, api_key=None, sync_config=None):
|
18
|
+
"""注册远程关键字服务器的便捷函数
|
19
|
+
|
20
|
+
Args:
|
21
|
+
url: 服务器URL
|
22
|
+
alias: 服务器别名
|
23
|
+
api_key: API密钥(可选)
|
24
|
+
sync_config: 变量同步配置(可选)
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
bool: 是否成功连接
|
28
|
+
"""
|
29
|
+
return remote_keyword_manager.register_remote_server(url, alias, api_key, sync_config)
|
30
|
+
|
31
|
+
|
32
|
+
def register_multiple_servers(servers_config):
|
33
|
+
"""批量注册远程服务器
|
34
|
+
|
35
|
+
Args:
|
36
|
+
servers_config: 服务器配置列表,每个配置包含url、alias等信息
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
dict: 注册结果,键为alias,值为是否成功
|
40
|
+
"""
|
41
|
+
results = {}
|
42
|
+
for server_config in servers_config:
|
43
|
+
if isinstance(server_config, dict):
|
44
|
+
url = server_config.get('url')
|
45
|
+
alias = server_config.get('alias')
|
46
|
+
api_key = server_config.get('api_key')
|
47
|
+
sync_config = server_config.get('sync_config')
|
48
|
+
|
49
|
+
if url and alias:
|
50
|
+
success = register_remote_server(
|
51
|
+
url, alias, api_key, sync_config)
|
52
|
+
results[alias] = success
|
53
|
+
|
54
|
+
return results
|
55
|
+
|
56
|
+
|
57
|
+
# 导出所有公共接口
|
58
|
+
__all__ = [
|
59
|
+
# 核心类
|
60
|
+
'remote_keyword_manager',
|
61
|
+
'RemoteKeywordManager',
|
62
|
+
'RemoteKeywordClient',
|
63
|
+
'RemoteKeywordServer',
|
64
|
+
'VariableBridge',
|
65
|
+
|
66
|
+
# 便捷函数
|
67
|
+
'register_remote_server',
|
68
|
+
'register_multiple_servers',
|
69
|
+
]
|
@@ -1,5 +1,5 @@
|
|
1
|
-
pytest_dsl/__init__.py,sha256=
|
2
|
-
pytest_dsl/cli.py,sha256=
|
1
|
+
pytest_dsl/__init__.py,sha256=kh7TLykNy37PBjppBv0qSXcQ-fCbLTE3apemmC8HYL0,6813
|
2
|
+
pytest_dsl/cli.py,sha256=kuCEp0FOmlbes8CreAugSwYyU8yoU_oG3HzSGSFh2Cs,13246
|
3
3
|
pytest_dsl/conftest_adapter.py,sha256=cevEb0oEZKTZfUrGe1-CmkFByxKhUtjuurBJP7kpLc0,149
|
4
4
|
pytest_dsl/main_adapter.py,sha256=pUIPN_EzY3JCDlYK7yF_OeLDVqni8vtG15G7gVzPJXg,181
|
5
5
|
pytest_dsl/plugin.py,sha256=CEwi-ci2rMevaAl9PwBw2WKXWRbXuHI1IkkDV0I0VIo,2224
|
@@ -7,29 +7,31 @@ pytest_dsl/core/__init__.py,sha256=ersUoxIWSrisxs9GX_STlH4XAbjNxAWUQB0NboaC5zI,3
|
|
7
7
|
pytest_dsl/core/auth_provider.py,sha256=IZfXXrr4Uuc8QHwRPvhHSzNa2fwrqhjYts1xh78D39Q,14861
|
8
8
|
pytest_dsl/core/auto_decorator.py,sha256=9Mga-GB4AzV5nkB6zpfaq8IuHa0KOH8LlFvnWyH_tnU,6623
|
9
9
|
pytest_dsl/core/auto_directory.py,sha256=egyTnVxtGs4P75EIivRauLRPJfN9aZpoGVvp_Ma72AM,2714
|
10
|
-
pytest_dsl/core/context.py,sha256=
|
11
|
-
pytest_dsl/core/custom_keyword_manager.py,sha256=
|
12
|
-
pytest_dsl/core/dsl_executor.py,sha256=
|
10
|
+
pytest_dsl/core/context.py,sha256=9F3aXuKkePO94mTZh4VKYoOeu4U2HKXzlgui4HtEz8c,3044
|
11
|
+
pytest_dsl/core/custom_keyword_manager.py,sha256=SSlsCv3GFMqmtORrqp2I70DvI5vjcBHUgofCgQATbtk,16272
|
12
|
+
pytest_dsl/core/dsl_executor.py,sha256=M6xEBaQOsjaUiiiPG-k-owRU7V5lXs3EPrrDGfDQzo8,61054
|
13
13
|
pytest_dsl/core/dsl_executor_utils.py,sha256=ZJLSYSsiKHqg53d3Bl--ZF8m9abd5kpqdevWl31KEZg,2276
|
14
14
|
pytest_dsl/core/execution_tracker.py,sha256=Pwcxraxt_xkOouq32KBqola-OVfnbaomCoMTyUIqoN4,9476
|
15
|
-
pytest_dsl/core/global_context.py,sha256=
|
15
|
+
pytest_dsl/core/global_context.py,sha256=1wU0I1fd5I9K3rX90hu6BaodynnYRQNFBFhcecO5eQE,4629
|
16
16
|
pytest_dsl/core/hook_manager.py,sha256=rqpcj9tBcxwFpoU2ZAfOWO8P0oyq02Ie_XUjq-9vFc0,2445
|
17
17
|
pytest_dsl/core/hookable_executor.py,sha256=sxkyWF7bSkKzgVoNwUTvtDGOSgFKXXQHR5uYIf-9MaM,3969
|
18
18
|
pytest_dsl/core/hookable_keyword_manager.py,sha256=e_guTVF1tU3pu_uzAUd4xNudFlYth42Ll2ESeRLlpjE,3833
|
19
19
|
pytest_dsl/core/hookspecs.py,sha256=fBmvs8uIV9_M_FUQBgvHSDn51Z6ABXo9k0okf80wzeo,5071
|
20
|
-
pytest_dsl/core/http_client.py,sha256=
|
20
|
+
pytest_dsl/core/http_client.py,sha256=h4RMCkfMYlKx2kdF53mcCJ-udux9q7LxsIeSXwXZt6o,16263
|
21
21
|
pytest_dsl/core/http_request.py,sha256=6e-gTztH3wu2eSW27Nc0uPmyWjB6oBwndx8Vqnu5uyg,60030
|
22
22
|
pytest_dsl/core/keyword_loader.py,sha256=3GQ4w5Zf2XdkoJ85uYXh9YB93_8L8OAb7vvuKE3-gVA,13864
|
23
23
|
pytest_dsl/core/keyword_manager.py,sha256=5WZWwlYk74kGHh1T6WjCuVd8eelq2-UEXvDIe4U7rEI,7730
|
24
|
-
pytest_dsl/core/keyword_utils.py,sha256=
|
24
|
+
pytest_dsl/core/keyword_utils.py,sha256=ONZxQJ0W7_knLGJpRdZvZ1srHLFybMuUZKI1wxoeaJA,20195
|
25
25
|
pytest_dsl/core/lexer.py,sha256=o_EJIadfhgyCImI73Y9ybqlBE9AisgA6nOhxpXNlaMw,4648
|
26
26
|
pytest_dsl/core/parser.py,sha256=SvTQ4jgMSe3MITSu9PftraElPAzVaBbNPHMEk1H_lFY,16597
|
27
27
|
pytest_dsl/core/parsetab.py,sha256=o4XbFKwpsi3fYmfI_F6u5NSM61Qp6gTx-Sfh1jDINxI,31767
|
28
28
|
pytest_dsl/core/plugin_discovery.py,sha256=3pt3EXJ7EPF0rkUlyDZMVHkIiTy2vicdIIQJkrHXZjY,8305
|
29
|
-
pytest_dsl/core/
|
29
|
+
pytest_dsl/core/remote_server_registry.py,sha256=MqAf2w0W_5D-zSClD87f9JDNQv-irZ4BrS03dcOFGU0,11046
|
30
|
+
pytest_dsl/core/utils.py,sha256=yAe-PtPTB7gSy8xa_V9UBk4L5SELvTEKiAhkiG4_2rM,5374
|
30
31
|
pytest_dsl/core/validator.py,sha256=2mjw7yiDEMu80FjJ_y2KCS-vA1Tb4kotrKkmLwpRe8Y,17420
|
31
|
-
pytest_dsl/core/
|
32
|
-
pytest_dsl/core/
|
32
|
+
pytest_dsl/core/variable_providers.py,sha256=ee81Pzy3GlU7q4taoSSd5E7YW87iPdusH0TfxV-0aUw,6198
|
33
|
+
pytest_dsl/core/variable_utils.py,sha256=5vB_0fnVaYyZ6rv23tv7kAyp_4xM8F3FIjxYrx3xen0,13927
|
34
|
+
pytest_dsl/core/yaml_loader.py,sha256=Lvut8-RKaVC4Gfv09XTVoagX1W1JKXgiJFMv8P8R9o0,14372
|
33
35
|
pytest_dsl/core/yaml_vars.py,sha256=PqbCGT_TmOXH09Pmm72sYtMEvV-sp9ocLqkuAUQYhhc,5047
|
34
36
|
pytest_dsl/docs/custom_keywords.md,sha256=03dA_GeHxoLixA8Sqch14bhTbxXQCSfz9Kvged2fMCo,4381
|
35
37
|
pytest_dsl/examples/__init__.py,sha256=FKkyLFOjxfC6XnJlW7iK_BUIX9dTIpXgoYN1pfs91ss,84
|
@@ -65,17 +67,17 @@ pytest_dsl/examples/quickstart/loops.auto,sha256=ZNZ6qP636v8QMY8QRyTUBB43gWCsqHb
|
|
65
67
|
pytest_dsl/keywords/__init__.py,sha256=5aiyPU_t1UiB2MEZ6M9ffOKnV1mFT_2YHxnZvyWaBNI,372
|
66
68
|
pytest_dsl/keywords/assertion_keywords.py,sha256=obW06H_3AizsvEM_9VE2JVuwvgrNVqP1kUTDd3U1SMk,23240
|
67
69
|
pytest_dsl/keywords/global_keywords.py,sha256=4yw5yeXoGf_4W26F39EA2Pp-mH9GiKGy2jKgFO9a_wM,2509
|
68
|
-
pytest_dsl/keywords/http_keywords.py,sha256=
|
70
|
+
pytest_dsl/keywords/http_keywords.py,sha256=NMOLqD9m7iNLIYKnkQiinZilgLNopd2QzxRgmnliaNc,28976
|
69
71
|
pytest_dsl/keywords/system_keywords.py,sha256=hjsACYER87rseSj4thBFnjDqe6At5hBT4Gjifj4ulDE,24470
|
70
|
-
pytest_dsl/remote/__init__.py,sha256=
|
72
|
+
pytest_dsl/remote/__init__.py,sha256=NyCyZ7VCjh0kJbdheSScNz-aA5UIFAJJXvvFIvoYCio,1844
|
71
73
|
pytest_dsl/remote/hook_manager.py,sha256=0hwRKP8yhcnfAnrrnZGVT-S0TBgo6c0A4qO5XRpvV1U,4899
|
72
74
|
pytest_dsl/remote/keyword_client.py,sha256=BL80MOaLroUi0v-9sLtkJ55g1R0Iw9SE1k11Ifwqx-I,17292
|
73
75
|
pytest_dsl/remote/keyword_server.py,sha256=vGIE3Bhh461xX_u1U-Cf5nrWL2GQFYdtQdcMWfFIYgE,22320
|
74
76
|
pytest_dsl/remote/variable_bridge.py,sha256=dv-d3Gq9ttvvrXM1fdlLtoSOPB6vRp0_GBOwX4wvcy8,7121
|
75
77
|
pytest_dsl/templates/keywords_report.html,sha256=7x84iq6hi08nf1iQ95jZ3izcAUPx6JFm0_8xS85CYws,31241
|
76
|
-
pytest_dsl-0.15.
|
77
|
-
pytest_dsl-0.15.
|
78
|
-
pytest_dsl-0.15.
|
79
|
-
pytest_dsl-0.15.
|
80
|
-
pytest_dsl-0.15.
|
81
|
-
pytest_dsl-0.15.
|
78
|
+
pytest_dsl-0.15.4.dist-info/licenses/LICENSE,sha256=Rguy8cb9sYhK6cmrBdXvwh94rKVDh2tVZEWptsHIsVM,1071
|
79
|
+
pytest_dsl-0.15.4.dist-info/METADATA,sha256=SFajau629WciJAm3w3txzx-KPxghyiCPTMpAVmSaYuM,29655
|
80
|
+
pytest_dsl-0.15.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
81
|
+
pytest_dsl-0.15.4.dist-info/entry_points.txt,sha256=PLOBbH02OGY1XR1JDKIZB1Em87loUvbgMRWaag-5FhY,204
|
82
|
+
pytest_dsl-0.15.4.dist-info/top_level.txt,sha256=4CrSx4uNqxj7NvK6k1y2JZrSrJSzi-UvPZdqpUhumWM,11
|
83
|
+
pytest_dsl-0.15.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|