pytest-dsl 0.15.3__py3-none-any.whl → 0.15.5__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.
@@ -0,0 +1,202 @@
1
+ """变量提供者模块
2
+
3
+ 定义了变量提供者的接口和具体实现,用于将不同来源的变量注入到TestContext中。
4
+ 这样可以实现解耦,让关键字只需要通过context获取变量,而不需要直接依赖特定的变量源。
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Optional, Dict
9
+
10
+
11
+ class VariableProvider(ABC):
12
+ """变量提供者接口
13
+
14
+ 所有的变量提供者都需要实现这个接口,以便可以注册到TestContext中。
15
+ """
16
+
17
+ @abstractmethod
18
+ def get_variable(self, key: str) -> Optional[Any]:
19
+ """获取变量值
20
+
21
+ Args:
22
+ key: 变量键
23
+
24
+ Returns:
25
+ 变量值,如果不存在返回None
26
+ """
27
+ pass
28
+
29
+ @abstractmethod
30
+ def has_variable(self, key: str) -> bool:
31
+ """检查变量是否存在
32
+
33
+ Args:
34
+ key: 变量键
35
+
36
+ Returns:
37
+ True如果变量存在,否则False
38
+ """
39
+ pass
40
+
41
+ def get_all_variables(self) -> Dict[str, Any]:
42
+ """获取所有变量(可选实现)
43
+
44
+ Returns:
45
+ 包含所有变量的字典
46
+ """
47
+ return {}
48
+
49
+
50
+ class YAMLVariableProvider(VariableProvider):
51
+ """YAML变量提供者
52
+
53
+ 将yaml_vars包装成变量提供者,使其可以注入到TestContext中。
54
+ """
55
+
56
+ def __init__(self):
57
+ # 延迟导入,避免循环依赖
58
+ from pytest_dsl.core.yaml_vars import yaml_vars
59
+ self.yaml_vars = yaml_vars
60
+
61
+ def get_variable(self, key: str) -> Optional[Any]:
62
+ """从YAML变量源获取变量值"""
63
+ return self.yaml_vars.get_variable(key)
64
+
65
+ def has_variable(self, key: str) -> bool:
66
+ """检查YAML变量源中是否存在变量"""
67
+ return self.yaml_vars.has_variable(key)
68
+
69
+ def get_all_variables(self) -> Dict[str, Any]:
70
+ """获取所有YAML变量"""
71
+ return self.yaml_vars.get_all_variables()
72
+
73
+
74
+ class GlobalContextVariableProvider(VariableProvider):
75
+ """全局上下文变量提供者
76
+
77
+ 将global_context包装成变量提供者,但需要避免和YAML变量重复。
78
+ """
79
+
80
+ def __init__(self):
81
+ # 延迟导入,避免循环依赖
82
+ from pytest_dsl.core.global_context import global_context
83
+ self.global_context = global_context
84
+
85
+ def get_variable(self, key: str) -> Optional[Any]:
86
+ """从全局上下文获取变量值"""
87
+ # 注意:global_context的get_variable方法内部也会调用yaml_vars
88
+ # 为了避免重复,这里直接访问存储的变量
89
+ try:
90
+ # 直接从全局变量存储中获取,跳过YAML变量
91
+ from filelock import FileLock
92
+ with FileLock(self.global_context._lock_file):
93
+ variables = self.global_context._load_variables()
94
+ return variables.get(key)
95
+ except Exception:
96
+ return None
97
+
98
+ def has_variable(self, key: str) -> bool:
99
+ """检查全局上下文中是否存在变量"""
100
+ try:
101
+ from filelock import FileLock
102
+ with FileLock(self.global_context._lock_file):
103
+ variables = self.global_context._load_variables()
104
+ return key in variables
105
+ except Exception:
106
+ return False
107
+
108
+ def get_all_variables(self) -> Dict[str, Any]:
109
+ """获取所有全局变量"""
110
+ try:
111
+ from filelock import FileLock
112
+ with FileLock(self.global_context._lock_file):
113
+ return self.global_context._load_variables()
114
+ except Exception:
115
+ return {}
116
+
117
+
118
+ class CompositeVariableProvider(VariableProvider):
119
+ """组合变量提供者
120
+
121
+ 可以将多个变量提供者组合在一起,按优先级顺序查找变量。
122
+ """
123
+
124
+ def __init__(self, providers: list = None):
125
+ """初始化组合变量提供者
126
+
127
+ Args:
128
+ providers: 变量提供者列表,按优先级排序(索引越小优先级越高)
129
+ """
130
+ self.providers = providers or []
131
+
132
+ def add_provider(self, provider: VariableProvider):
133
+ """添加变量提供者"""
134
+ if provider not in self.providers:
135
+ self.providers.append(provider)
136
+
137
+ def remove_provider(self, provider: VariableProvider):
138
+ """移除变量提供者"""
139
+ if provider in self.providers:
140
+ self.providers.remove(provider)
141
+
142
+ def get_variable(self, key: str) -> Optional[Any]:
143
+ """按优先级顺序从提供者中获取变量"""
144
+ for provider in self.providers:
145
+ try:
146
+ value = provider.get_variable(key)
147
+ if value is not None:
148
+ return value
149
+ except Exception:
150
+ continue
151
+ return None
152
+
153
+ def has_variable(self, key: str) -> bool:
154
+ """检查是否有任何提供者包含该变量"""
155
+ for provider in self.providers:
156
+ try:
157
+ if provider.has_variable(key):
158
+ return True
159
+ except Exception:
160
+ continue
161
+ return False
162
+
163
+ def get_all_variables(self) -> Dict[str, Any]:
164
+ """获取所有变量,优先级高的覆盖优先级低的"""
165
+ all_vars = {}
166
+
167
+ # 从后往前遍历,让优先级高的覆盖优先级低的
168
+ for provider in reversed(self.providers):
169
+ try:
170
+ vars_dict = provider.get_all_variables()
171
+ all_vars.update(vars_dict)
172
+ except Exception:
173
+ continue
174
+
175
+ return all_vars
176
+
177
+
178
+ # 创建默认的变量提供者实例
179
+ def create_default_variable_providers() -> list:
180
+ """创建默认的变量提供者列表
181
+
182
+ 按优先级排序:YAML变量 > 全局上下文变量
183
+
184
+ Returns:
185
+ 变量提供者列表
186
+ """
187
+ providers = [
188
+ YAMLVariableProvider(),
189
+ GlobalContextVariableProvider()
190
+ ]
191
+ return providers
192
+
193
+
194
+ def setup_context_with_default_providers(context):
195
+ """为TestContext设置默认的变量提供者
196
+
197
+ Args:
198
+ context: TestContext实例
199
+ """
200
+ providers = create_default_variable_providers()
201
+ for provider in providers:
202
+ context.register_external_variable_provider(provider)
@@ -1,50 +1,54 @@
1
+ """变量替换工具模块
2
+
3
+ 该模块提供了高级的变量替换功能,支持复杂的变量访问语法。
4
+ """
5
+
1
6
  import re
2
7
  import json
3
- from typing import Any, Dict, List, Union
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 or TestContext()
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
- 如果上下文对象中包含executor属性,则使用executor的上下文
31
- (这确保即使上下文被替换也能获取正确的引用)
32
-
33
- Returns:
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
- 变量值,如果变量不存在则抛出 KeyError
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
- if self.test_context.has(var_name):
59
- value = self.test_context.get(var_name)
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
- # YAML变量中获取
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) + result[match.end():]
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(current_value, part, root_var_name)
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(f"无法在 {type(current_value).__name__} 类型上使用字符串键访问")
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(f"数组索引 {index} 超出范围,数组长度为 {len(current_value)}")
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(f"无法在 {type(current_value).__name__} 类型上使用索引访问")
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(f"无法访问属性 '{access_token}',当前值类型是 {type(current_value).__name__}")
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(key) if isinstance(key, str) else key
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)
@@ -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
- """从参数加载YAML变量文件(通用函数)
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: 项目根目录(用于默认config目录)
41
+ project_root: 项目根目录
41
42
  environment: 环境名称(用于hook加载)
43
+ auto_load_default: 是否自动加载默认配置
44
+ None - 根据用户输入智能判断
45
+ True - 强制加载默认配置
46
+ False - 不加载默认配置
42
47
  """
43
- # 首先尝试通过hook加载变量
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
- # 加载单个YAML文件
70
+ # 加载用户指定的YAML文件(第二优先级)
53
71
  if yaml_files:
54
72
  yaml_vars.load_yaml_files(yaml_files)
55
- print(f"已加载YAML变量文件: {', '.join(yaml_files)}")
56
-
57
- # 加载目录中的YAML文件
58
- if yaml_vars_dir is None and project_root:
59
- # 默认使用项目根目录下的config目录
60
- yaml_vars_dir = str(Path(project_root) / 'config')
61
- print(f"使用默认YAML变量目录: {yaml_vars_dir}")
62
-
63
- if yaml_vars_dir and Path(yaml_vars_dir).exists():
64
- yaml_vars.load_from_directory(yaml_vars_dir)
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
- else:
75
- print(f"加载的文件: {', '.join(loaded_files)}")
76
- elif yaml_vars_dir:
77
- print(f"YAML变量目录不存在: {yaml_vars_dir}")
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
- remote_servers = yaml_vars.get_variable('remote_servers')
213
- if not remote_servers:
214
- return
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
- print(f"发现 {len(remote_servers)} 个远程服务器配置")
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 remote_servers:
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
- pass
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
- # 检查YAML变量中的http_clients配置(现在包含同步的变量)
297
- http_clients_config = yaml_vars.get_variable("http_clients")
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
- # 从YAML变量中获取模板
316
- http_templates = yaml_vars.get_variable("http_templates") or {}
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: