pytest-dsl 0.9.0__py3-none-any.whl → 0.10.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.
@@ -0,0 +1,164 @@
1
+ """远程服务器变量桥接模块
2
+
3
+ 该模块提供了变量桥接机制,让远程服务器中的关键字能够无缝访问客户端同步的变量。
4
+ 通过hook机制拦截变量访问,实现变量的透明传递。
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Optional
9
+ from pytest_dsl.remote.hook_manager import register_startup_hook, register_before_keyword_hook
10
+ from pytest_dsl.core.yaml_vars import yaml_vars
11
+ from pytest_dsl.core.global_context import global_context
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class VariableBridge:
17
+ """变量桥接器,负责在远程服务器中桥接客户端同步的变量"""
18
+
19
+ def __init__(self):
20
+ self.shared_variables = {} # 引用远程服务器的shared_variables
21
+ self.original_yaml_get_variable = None
22
+ self.original_global_get_variable = None
23
+ self._bridge_installed = False
24
+
25
+ def install_bridge(self, shared_variables: dict):
26
+ """安装变量桥接机制
27
+
28
+ Args:
29
+ shared_variables: 远程服务器的共享变量字典
30
+ """
31
+ if self._bridge_installed:
32
+ return
33
+
34
+ self.shared_variables = shared_variables
35
+
36
+ # 备份原始方法
37
+ self.original_yaml_get_variable = yaml_vars.get_variable
38
+ self.original_global_get_variable = global_context.get_variable
39
+
40
+ # 安装桥接方法
41
+ yaml_vars.get_variable = self._bridged_yaml_get_variable
42
+ global_context.get_variable = self._bridged_global_get_variable
43
+
44
+ self._bridge_installed = True
45
+ logger.info("变量桥接机制已安装")
46
+
47
+ def _bridged_yaml_get_variable(self, name: str) -> Optional[Any]:
48
+ """桥接的YAML变量获取方法
49
+
50
+ 优先级:
51
+ 1. 原始YAML变量
52
+ 2. 客户端同步的变量
53
+ """
54
+ # 首先尝试从原始YAML变量获取
55
+ original_value = self.original_yaml_get_variable(name)
56
+ if original_value is not None:
57
+ return original_value
58
+
59
+ # 如果原始YAML中没有,尝试从同步变量获取
60
+ if name in self.shared_variables:
61
+ logger.debug(f"从同步变量获取YAML变量: {name}")
62
+ return self.shared_variables[name]
63
+
64
+ return None
65
+
66
+ def _bridged_global_get_variable(self, name: str) -> Any:
67
+ """桥接的全局变量获取方法
68
+
69
+ 优先级:
70
+ 1. 原始全局变量(包括YAML变量)
71
+ 2. 客户端同步的变量
72
+ """
73
+ try:
74
+ # 首先尝试从原始全局上下文获取
75
+ original_value = self.original_global_get_variable(name)
76
+ if original_value is not None:
77
+ return original_value
78
+ except:
79
+ # 如果原始方法抛出异常,继续尝试同步变量
80
+ pass
81
+
82
+ # 如果原始全局变量中没有,尝试从同步变量获取
83
+ if name in self.shared_variables:
84
+ logger.debug(f"从同步变量获取全局变量: {name}")
85
+ return self.shared_variables[name]
86
+
87
+ # 如果都没有找到,返回None(保持原有行为)
88
+ return None
89
+
90
+ def uninstall_bridge(self):
91
+ """卸载变量桥接机制"""
92
+ if not self._bridge_installed:
93
+ return
94
+
95
+ # 恢复原始方法
96
+ if self.original_yaml_get_variable:
97
+ yaml_vars.get_variable = self.original_yaml_get_variable
98
+ if self.original_global_get_variable:
99
+ global_context.get_variable = self.original_global_get_variable
100
+
101
+ self._bridge_installed = False
102
+ logger.info("变量桥接机制已卸载")
103
+
104
+
105
+ # 全局变量桥接器实例
106
+ variable_bridge = VariableBridge()
107
+
108
+
109
+ @register_startup_hook
110
+ def setup_variable_bridge(context):
111
+ """服务器启动时安装变量桥接机制"""
112
+ shared_variables = context.get('shared_variables')
113
+ if shared_variables is not None:
114
+ variable_bridge.install_bridge(shared_variables)
115
+ logger.info("变量桥接机制已在服务器启动时安装")
116
+ else:
117
+ logger.warning("无法获取shared_variables,变量桥接机制安装失败")
118
+
119
+
120
+ @register_before_keyword_hook
121
+ def ensure_variable_bridge(context):
122
+ """关键字执行前确保变量桥接机制正常工作"""
123
+ # 这个hook主要用于调试和监控
124
+ shared_variables = context.get('shared_variables')
125
+ keyword_name = context.get('keyword_name')
126
+
127
+ # 只对特定关键字进行调试日志
128
+ if keyword_name in ['HTTP请求', '数据库查询'] and shared_variables:
129
+ synced_count = len(shared_variables)
130
+ if synced_count > 0:
131
+ logger.debug(f"关键字 {keyword_name} 执行前,可用同步变量数量: {synced_count}")
132
+
133
+
134
+ def get_synced_variable(name: str) -> Optional[Any]:
135
+ """直接从同步变量中获取变量值
136
+
137
+ Args:
138
+ name: 变量名
139
+
140
+ Returns:
141
+ 变量值,如果不存在则返回None
142
+ """
143
+ return variable_bridge.shared_variables.get(name)
144
+
145
+
146
+ def list_synced_variables() -> dict:
147
+ """列出所有同步的变量
148
+
149
+ Returns:
150
+ 同步变量字典的副本
151
+ """
152
+ return variable_bridge.shared_variables.copy()
153
+
154
+
155
+ def has_synced_variable(name: str) -> bool:
156
+ """检查是否存在指定的同步变量
157
+
158
+ Args:
159
+ name: 变量名
160
+
161
+ Returns:
162
+ 是否存在该同步变量
163
+ """
164
+ return name in variable_bridge.shared_variables
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-dsl
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: A DSL testing framework based on pytest
5
5
  Author: Chen Shuanglin
6
6
  License: MIT
@@ -42,11 +42,12 @@ pytest-dsl是一个革命性的关键字驱动测试框架,基于pytest构建
42
42
  ## ✨ 核心特性
43
43
 
44
44
  - 🎯 **门槛上手低** - 自然语言风格,只需少量编程基础
45
- - 🔧 **高度可扩展** - 轻松创建自定义关键字
45
+ - 🔧 **高度可扩展** - 轻松创建自定义关键字,支持参数默认值
46
46
  - 🌐 **分布式执行** - 支持远程关键字调用
47
47
  - 🔄 **无缝集成** - 完美兼容pytest生态
48
48
  - 📊 **丰富报告** - 集成Allure测试报告
49
49
  - 🛡️ **企业级** - 支持变量管理、环境隔离
50
+ - ⚡ **智能简化** - 参数默认值让DSL更加简洁易读
50
51
 
51
52
  ## 🚀 5分钟快速开始
52
53
 
@@ -850,17 +851,19 @@ def database_query(**kwargs):
850
851
 
851
852
  @keyword_manager.register('发送邮件', [
852
853
  {'name': '收件人', 'mapping': 'to_email', 'description': '收件人邮箱'},
853
- {'name': '主题', 'mapping': 'subject', 'description': '邮件主题'},
854
- {'name': '内容', 'mapping': 'content', 'description': '邮件内容'}
854
+ {'name': '主题', 'mapping': 'subject', 'description': '邮件主题', 'default': '测试邮件'},
855
+ {'name': '内容', 'mapping': 'content', 'description': '邮件内容', 'default': '这是一封测试邮件'},
856
+ {'name': '优先级', 'mapping': 'priority', 'description': '邮件优先级', 'default': 'normal'}
855
857
  ])
856
858
  def send_email(**kwargs):
857
859
  """发送邮件通知"""
858
860
  to_email = kwargs.get('to_email')
859
- subject = kwargs.get('subject')
860
- content = kwargs.get('content')
861
+ subject = kwargs.get('subject', '测试邮件')
862
+ content = kwargs.get('content', '这是一封测试邮件')
863
+ priority = kwargs.get('priority', 'normal')
861
864
 
862
865
  # 实现邮件发送逻辑
863
- print(f"发送邮件到 {to_email}: {subject}")
866
+ print(f"发送邮件到 {to_email}: {subject} (优先级: {priority})")
864
867
 
865
868
  return True
866
869
  ```
@@ -874,10 +877,71 @@ def send_email(**kwargs):
874
877
  users = [数据库查询], 查询语句: "SELECT * FROM users WHERE active = 1"
875
878
  [打印], 内容: "查询到 ${len(users)} 个活跃用户"
876
879
 
877
- # 发送测试报告邮件
878
- [发送邮件], 收件人: "admin@example.com", 主题: "测试报告", 内容: "测试已完成"
880
+ # 发送测试报告邮件 - 使用默认值
881
+ [发送邮件], 收件人: "admin@example.com" # 主题和内容使用默认值
882
+
883
+ # 发送自定义邮件 - 覆盖默认值
884
+ [发送邮件], 收件人: "dev@example.com", 主题: "部署完成", 内容: "系统已成功部署到生产环境"
885
+ ```
886
+
887
+ ### 5. 参数默认值功能 🆕
888
+
889
+ pytest-dsl 现在支持为关键字参数设置默认值,让DSL编写更加简洁:
890
+
891
+ #### 定义带默认值的关键字
892
+
893
+ ```python
894
+ from pytest_dsl.core.keyword_manager import keyword_manager
895
+
896
+ @keyword_manager.register('HTTP请求', [
897
+ {'name': '地址', 'mapping': 'url', 'description': '请求地址'},
898
+ {'name': '方法', 'mapping': 'method', 'description': 'HTTP方法', 'default': 'GET'},
899
+ {'name': '超时', 'mapping': 'timeout', 'description': '超时时间(秒)', 'default': 30},
900
+ {'name': '重试次数', 'mapping': 'retries', 'description': '重试次数', 'default': 3},
901
+ {'name': '验证SSL', 'mapping': 'verify_ssl', 'description': '是否验证SSL证书', 'default': True}
902
+ ])
903
+ def http_request(**kwargs):
904
+ """HTTP请求关键字,支持默认值"""
905
+ url = kwargs.get('url')
906
+ method = kwargs.get('method', 'GET') # 默认值也会自动应用
907
+ timeout = kwargs.get('timeout', 30)
908
+ retries = kwargs.get('retries', 3)
909
+ verify_ssl = kwargs.get('verify_ssl', True)
910
+
911
+ # 执行HTTP请求逻辑
912
+ return {"status": "success", "method": method, "url": url}
879
913
  ```
880
914
 
915
+ #### 在DSL中使用默认值
916
+
917
+ ```python
918
+ @name: "默认值功能演示"
919
+
920
+ # 只传递必需参数,其他使用默认值
921
+ response1 = [HTTP请求], 地址: "https://api.example.com/users"
922
+ # 等价于:方法: "GET", 超时: 30, 重试次数: 3, 验证SSL: True
923
+
924
+ # 部分覆盖默认值
925
+ response2 = [HTTP请求], 地址: "https://api.example.com/users", 方法: "POST", 超时: 60
926
+ # 只覆盖方法和超时,重试次数和SSL验证仍使用默认值
927
+
928
+ # 内置关键字也支持默认值
929
+ random_num = [生成随机数] # 使用默认范围 0-100,整数
930
+ custom_num = [生成随机数], 最大值: 50 # 只修改最大值,其他保持默认
931
+
932
+ # 生成随机字符串
933
+ default_string = [生成随机字符串] # 长度8,字母数字混合
934
+ custom_string = [生成随机字符串], 长度: 12, 类型: "letters" # 自定义长度和类型
935
+ ```
936
+
937
+ #### 默认值的优势
938
+
939
+ - **🎯 简化调用** - 只需传递关键参数,常用配置自动应用
940
+ - **🔧 灵活覆盖** - 可选择性地覆盖任何默认值
941
+ - **📖 提高可读性** - DSL更加简洁,重点突出
942
+ - **🛡️ 减少错误** - 避免重复配置常用参数
943
+ - **🌐 远程支持** - 远程关键字也完整支持默认值功能
944
+
881
945
  #### 支持远程模式的关键字
882
946
 
883
947
  ```python
@@ -1037,20 +1101,6 @@ CMD ["pytest-dsl", "tests/", "--yaml-vars", "config/prod.yaml"]
1037
1101
  - **集成测试** - 跨系统测试协调
1038
1102
  - **性能测试** - 结合其他工具进行性能测试
1039
1103
 
1040
- ## 🤝 贡献与支持
1041
-
1042
- 我们欢迎您的贡献和反馈!
1043
-
1044
- - 🐛 [报告问题](https://github.com/your-repo/pytest-dsl/issues)
1045
- - 💡 [功能建议](https://github.com/your-repo/pytest-dsl/discussions)
1046
- - 🔧 [提交PR](https://github.com/your-repo/pytest-dsl/pulls)
1047
-
1048
- ## 📄 许可证
1049
-
1050
- MIT License - 详见 [LICENSE](LICENSE) 文件
1051
-
1052
- ---
1053
-
1054
1104
  ## 📋 示例验证
1055
1105
 
1056
1106
  本README.md中的大部分示例都已经过验证,确保可以正常运行。验证示例位于 `examples/readme_validation/` 目录中。
@@ -1086,3 +1136,18 @@ pytest-dsl api_basic.dsl
1086
1136
  ---
1087
1137
 
1088
1138
  🚀 **开始使用pytest-dsl,让测试自动化变得简单而强大!**
1139
+
1140
+
1141
+ ## 🤝 贡献与支持
1142
+
1143
+ 我们欢迎您的贡献和反馈!
1144
+
1145
+ - 🐛 [报告问题](https://github.com/felix-1991/pytest-dsl/issues)
1146
+ - 💡 [功能建议](https://github.com/felix-1991/pytest-dsl/discussions)
1147
+ - 🔧 [提交PR](https://github.com/felix-1991/pytest-dsl/pulls)
1148
+
1149
+ ## 📄 许可证
1150
+
1151
+ MIT License - 详见 [LICENSE](LICENSE) 文件
1152
+
1153
+ ---
@@ -1,5 +1,5 @@
1
1
  pytest_dsl/__init__.py,sha256=FzwXGvmuvMhRBKxvCdh1h-yJ2wUOnDxcTbU4Nt5fHn8,301
2
- pytest_dsl/cli.py,sha256=4X-nq7TdBgFGN9oDBhgNHaoLBmTV0WQ_uxNY6cdRS4M,14944
2
+ pytest_dsl/cli.py,sha256=1lsjCkfRk-goGoi4E37FjFTGrg74idyB1ktV2J1F2IM,17763
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=MEQcdK0xdxwxCxPEDLNHX_kGF9Jc7bNxlNi4mx588DU,1190
@@ -14,7 +14,7 @@ pytest_dsl/core/dsl_executor_utils.py,sha256=cFoR2p3qQ2pb-UhkoefleK-zbuFqf0aBLh2
14
14
  pytest_dsl/core/global_context.py,sha256=NcEcS2V61MT70tgAsGsFWQq0P3mKjtHQr1rgT3yTcyY,3535
15
15
  pytest_dsl/core/http_client.py,sha256=1AHqtM_fkXf8JrM0ljMsJwUkyt-ysjR16NoyCckHfGc,15810
16
16
  pytest_dsl/core/http_request.py,sha256=nGMlx0mFc7rDLIdp9GJ3e09OQH3ryUA56yJdRZ99dOQ,57445
17
- pytest_dsl/core/keyword_manager.py,sha256=FtPsXlI7PxvVQMJfDN_nQYvRhkag5twvaHXjELQsCEo,4068
17
+ pytest_dsl/core/keyword_manager.py,sha256=vm78au2s8waWD7k2sxltkOhJNgEJOGccDBTr8fgN3eU,4773
18
18
  pytest_dsl/core/lexer.py,sha256=WaLzt9IhtHiA90Fg2WGgfVztveCUhtgxzANBaEiy-F8,4347
19
19
  pytest_dsl/core/parser.py,sha256=xxy6yC6NdwHxln200aIuaWWN3w44uI8kkNlw8PTVpYI,11855
20
20
  pytest_dsl/core/parsetab.py,sha256=aN-2RRTr3MSbMyfe-9zOj_t96Xu84avE29GWqH6nqmg,31472
@@ -55,13 +55,18 @@ pytest_dsl/examples/quickstart/api_basics.auto,sha256=SrDRBASqy5ZMXnmfczuKNCXoTi
55
55
  pytest_dsl/examples/quickstart/assertions.auto,sha256=FWrwod3L8oxuKCnA-lnVO2fzs5bZsXWaVqd6zehKrzw,859
56
56
  pytest_dsl/examples/quickstart/loops.auto,sha256=ZNZ6qP636v8QMY8QRyTUBB43gWCsqHbpPQw2RqamvOk,516
57
57
  pytest_dsl/keywords/__init__.py,sha256=5aiyPU_t1UiB2MEZ6M9ffOKnV1mFT_2YHxnZvyWaBNI,372
58
- pytest_dsl/keywords/assertion_keywords.py,sha256=WOCGP7WX2wZ6mQPDGmi38LWdG2NaThHoNU54xc8VpxI,23027
58
+ pytest_dsl/keywords/assertion_keywords.py,sha256=obW06H_3AizsvEM_9VE2JVuwvgrNVqP1kUTDd3U1SMk,23240
59
59
  pytest_dsl/keywords/global_keywords.py,sha256=4yw5yeXoGf_4W26F39EA2Pp-mH9GiKGy2jKgFO9a_wM,2509
60
- pytest_dsl/keywords/http_keywords.py,sha256=DY1SvUgOziN6Ga-QgE0q4XFd2qGGwvbv1B_k2gTdPLw,25554
61
- pytest_dsl/keywords/system_keywords.py,sha256=n_jRrMvSv2v6Pm_amokfyLNVOLYP7CFWbBE3_dlO7h4,11299
62
- pytest_dsl-0.9.0.dist-info/licenses/LICENSE,sha256=Rguy8cb9sYhK6cmrBdXvwh94rKVDh2tVZEWptsHIsVM,1071
63
- pytest_dsl-0.9.0.dist-info/METADATA,sha256=it5pFZgvhb4OBllvC5vPxn-rezLeMsvGVtjPrtEDw6g,26712
64
- pytest_dsl-0.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
- pytest_dsl-0.9.0.dist-info/entry_points.txt,sha256=vuYIReCjHhS_cpaPAbQxOnFavQAwW0K66dKxUQSsRvc,190
66
- pytest_dsl-0.9.0.dist-info/top_level.txt,sha256=4CrSx4uNqxj7NvK6k1y2JZrSrJSzi-UvPZdqpUhumWM,11
67
- pytest_dsl-0.9.0.dist-info/RECORD,,
60
+ pytest_dsl/keywords/http_keywords.py,sha256=B2IoWZjmAk0bQf1o5hHhHEDHpAl010PrDVA4iJ1mLk0,25605
61
+ pytest_dsl/keywords/system_keywords.py,sha256=e65Mzyt56FYmw0vHegajLUSLeEYVI9Y-WSB1h6SKKLo,12089
62
+ pytest_dsl/remote/__init__.py,sha256=syRSxTlTUfdAPleJnVS4MykRyEN8_SKiqlsn6SlIK8k,120
63
+ pytest_dsl/remote/hook_manager.py,sha256=0hwRKP8yhcnfAnrrnZGVT-S0TBgo6c0A4qO5XRpvV1U,4899
64
+ pytest_dsl/remote/keyword_client.py,sha256=doXmg2Aenx9SclKtd5bT3n-DkxGK3MnMRwn4dpdug8s,16848
65
+ pytest_dsl/remote/keyword_server.py,sha256=7eRBHXyTC2AyXBeXMHOYLjgj570ioiAhnq4CuEnkak4,21386
66
+ pytest_dsl/remote/variable_bridge.py,sha256=KG2xTZcUExb8kNL-rR-IPFJmYGfo7ciE4W7xzet59sw,5520
67
+ pytest_dsl-0.10.0.dist-info/licenses/LICENSE,sha256=Rguy8cb9sYhK6cmrBdXvwh94rKVDh2tVZEWptsHIsVM,1071
68
+ pytest_dsl-0.10.0.dist-info/METADATA,sha256=wzdKD6pqnD0Xj4xP2GlWg5R0r5JewGH2hk7-rHTadZs,29613
69
+ pytest_dsl-0.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
+ pytest_dsl-0.10.0.dist-info/entry_points.txt,sha256=PLOBbH02OGY1XR1JDKIZB1Em87loUvbgMRWaag-5FhY,204
71
+ pytest_dsl-0.10.0.dist-info/top_level.txt,sha256=4CrSx4uNqxj7NvK6k1y2JZrSrJSzi-UvPZdqpUhumWM,11
72
+ pytest_dsl-0.10.0.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  [console_scripts]
2
2
  pytest-dsl = pytest_dsl.cli:main
3
- pytest-dsl-list = pytest_dsl.cli:main
3
+ pytest-dsl-list = pytest_dsl.cli:main_list_keywords
4
4
  pytest-dsl-server = pytest_dsl.remote.keyword_server:main
5
5
 
6
6
  [pytest11]