pytest-dsl 0.15.0__py3-none-any.whl → 0.15.2__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 +12 -0
- pytest_dsl/cli.py +15 -427
- pytest_dsl/core/dsl_executor.py +598 -339
- pytest_dsl/core/keyword_utils.py +609 -0
- pytest_dsl/core/lexer.py +8 -0
- pytest_dsl/core/parser.py +55 -3
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/METADATA +1 -1
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/RECORD +12 -11
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/WHEEL +0 -0
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.15.0.dist-info → pytest_dsl-0.15.2.dist-info}/top_level.txt +0 -0
pytest_dsl/__init__.py
CHANGED
@@ -54,6 +54,13 @@ from pytest_dsl.core.keyword_loader import (
|
|
54
54
|
group_keywords_by_source, scan_project_custom_keywords
|
55
55
|
)
|
56
56
|
|
57
|
+
# 关键字工具
|
58
|
+
from pytest_dsl.core.keyword_utils import (
|
59
|
+
KeywordInfo, KeywordListOptions, KeywordFormatter, KeywordLister,
|
60
|
+
keyword_lister, list_keywords, get_keyword_info, search_keywords,
|
61
|
+
generate_html_report
|
62
|
+
)
|
63
|
+
|
57
64
|
# 便捷导入的别名
|
58
65
|
Executor = DSLExecutor
|
59
66
|
Validator = DSLValidator
|
@@ -77,6 +84,11 @@ __all__ = [
|
|
77
84
|
'load_all_keywords', 'categorize_keyword', 'get_keyword_source_info',
|
78
85
|
'group_keywords_by_source', 'scan_project_custom_keywords',
|
79
86
|
|
87
|
+
# 关键字工具
|
88
|
+
'KeywordInfo', 'KeywordListOptions', 'KeywordFormatter', 'KeywordLister',
|
89
|
+
'keyword_lister', 'list_keywords', 'get_keyword_info', 'search_keywords',
|
90
|
+
'generate_html_report',
|
91
|
+
|
80
92
|
# Hook系统
|
81
93
|
'hookimpl', 'hookspec', 'DSLHookSpecs',
|
82
94
|
'hook_manager', 'DSLHookManager', 'HookManager',
|
pytest_dsl/cli.py
CHANGED
@@ -16,11 +16,8 @@ from pytest_dsl.core.yaml_loader import load_yaml_variables_from_args
|
|
16
16
|
from pytest_dsl.core.auto_directory import (
|
17
17
|
SETUP_FILE_NAME, TEARDOWN_FILE_NAME, execute_hook_file
|
18
18
|
)
|
19
|
-
from pytest_dsl.core.keyword_loader import
|
20
|
-
|
21
|
-
group_keywords_by_source, scan_project_custom_keywords
|
22
|
-
)
|
23
|
-
from pytest_dsl.core.keyword_manager import keyword_manager
|
19
|
+
from pytest_dsl.core.keyword_loader import load_all_keywords
|
20
|
+
from pytest_dsl.core.keyword_utils import list_keywords as utils_list_keywords
|
24
21
|
|
25
22
|
|
26
23
|
def read_file(filename):
|
@@ -139,431 +136,25 @@ def parse_args():
|
|
139
136
|
return args
|
140
137
|
|
141
138
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
|
146
|
-
project_custom_keywords=None):
|
147
|
-
"""格式化关键字信息为文本格式"""
|
148
|
-
lines = []
|
149
|
-
|
150
|
-
# 关键字名称和类别
|
151
|
-
category = categorize_keyword(
|
152
|
-
keyword_name, keyword_info, project_custom_keywords
|
153
|
-
)
|
154
|
-
category_names = {
|
155
|
-
'builtin': '内置',
|
156
|
-
'custom': '自定义',
|
157
|
-
'project_custom': '项目自定义',
|
158
|
-
'remote': '远程'
|
159
|
-
}
|
160
|
-
|
161
|
-
if show_category:
|
162
|
-
category_display = category_names.get(category, '未知')
|
163
|
-
lines.append(f"关键字: {keyword_name} [{category_display}]")
|
164
|
-
else:
|
165
|
-
lines.append(f"关键字: {keyword_name}")
|
166
|
-
|
167
|
-
# 远程关键字特殊标识
|
168
|
-
if keyword_info.get('remote', False):
|
169
|
-
alias = keyword_info.get('alias', '未知')
|
170
|
-
original_name = keyword_info.get('original_name', keyword_name)
|
171
|
-
lines.append(f" 远程服务器: {alias}")
|
172
|
-
lines.append(f" 原始名称: {original_name}")
|
173
|
-
|
174
|
-
# 项目自定义关键字特殊标识
|
175
|
-
if category == 'project_custom' and project_custom_keywords:
|
176
|
-
custom_info = project_custom_keywords[keyword_name]
|
177
|
-
lines.append(f" 文件位置: {custom_info['file']}")
|
178
|
-
|
179
|
-
# 对于项目自定义关键字,使用从AST中提取的参数信息
|
180
|
-
custom_parameters = custom_info.get('parameters', [])
|
181
|
-
if custom_parameters:
|
182
|
-
lines.append(" 参数:")
|
183
|
-
for param_info in custom_parameters:
|
184
|
-
param_name = param_info['name']
|
185
|
-
param_mapping = param_info.get('mapping', '')
|
186
|
-
param_desc = param_info.get('description', '')
|
187
|
-
param_default = param_info.get('default', None)
|
188
|
-
|
189
|
-
# 构建参数描述
|
190
|
-
param_parts = []
|
191
|
-
if param_mapping and param_mapping != param_name:
|
192
|
-
param_parts.append(f"{param_name} ({param_mapping})")
|
193
|
-
else:
|
194
|
-
param_parts.append(param_name)
|
195
|
-
|
196
|
-
param_parts.append(f": {param_desc}")
|
197
|
-
|
198
|
-
# 添加默认值信息
|
199
|
-
if param_default is not None:
|
200
|
-
param_parts.append(f" (默认值: {param_default})")
|
201
|
-
|
202
|
-
lines.append(f" {''.join(param_parts)}")
|
203
|
-
else:
|
204
|
-
lines.append(" 参数: 无")
|
205
|
-
else:
|
206
|
-
# 参数信息(对于其他类型的关键字)
|
207
|
-
parameters = keyword_info.get('parameters', [])
|
208
|
-
if parameters:
|
209
|
-
lines.append(" 参数:")
|
210
|
-
for param in parameters:
|
211
|
-
param_name = getattr(param, 'name', str(param))
|
212
|
-
param_mapping = getattr(param, 'mapping', '')
|
213
|
-
param_desc = getattr(param, 'description', '')
|
214
|
-
param_default = getattr(param, 'default', None)
|
215
|
-
|
216
|
-
# 构建参数描述
|
217
|
-
param_info = []
|
218
|
-
if param_mapping and param_mapping != param_name:
|
219
|
-
param_info.append(f"{param_name} ({param_mapping})")
|
220
|
-
else:
|
221
|
-
param_info.append(param_name)
|
222
|
-
|
223
|
-
param_info.append(f": {param_desc}")
|
224
|
-
|
225
|
-
# 添加默认值信息
|
226
|
-
if param_default is not None:
|
227
|
-
param_info.append(f" (默认值: {param_default})")
|
228
|
-
|
229
|
-
lines.append(f" {''.join(param_info)}")
|
230
|
-
else:
|
231
|
-
lines.append(" 参数: 无")
|
232
|
-
|
233
|
-
# 函数文档
|
234
|
-
func = keyword_info.get('func')
|
235
|
-
if func and hasattr(func, '__doc__') and func.__doc__:
|
236
|
-
lines.append(f" 说明: {func.__doc__.strip()}")
|
237
|
-
|
238
|
-
return '\n'.join(lines)
|
239
|
-
|
240
|
-
|
241
|
-
def format_keyword_info_json(keyword_name, keyword_info,
|
242
|
-
project_custom_keywords=None):
|
243
|
-
"""格式化关键字信息为JSON格式"""
|
244
|
-
category = categorize_keyword(
|
245
|
-
keyword_name, keyword_info, project_custom_keywords
|
246
|
-
)
|
247
|
-
source_info = get_keyword_source_info(keyword_info)
|
248
|
-
|
249
|
-
keyword_data = {
|
250
|
-
'name': keyword_name,
|
251
|
-
'category': category,
|
252
|
-
'source_info': source_info,
|
253
|
-
'parameters': []
|
254
|
-
}
|
255
|
-
|
256
|
-
# 远程关键字特殊信息
|
257
|
-
if keyword_info.get('remote', False):
|
258
|
-
keyword_data['remote'] = {
|
259
|
-
'alias': keyword_info.get('alias', ''),
|
260
|
-
'original_name': keyword_info.get('original_name', keyword_name)
|
261
|
-
}
|
262
|
-
|
263
|
-
# 项目自定义关键字特殊信息
|
264
|
-
if category == 'project_custom' and project_custom_keywords:
|
265
|
-
custom_info = project_custom_keywords[keyword_name]
|
266
|
-
keyword_data['file_location'] = custom_info['file']
|
267
|
-
|
268
|
-
# 对于项目自定义关键字,使用从AST中提取的参数信息
|
269
|
-
for param_info in custom_info.get('parameters', []):
|
270
|
-
keyword_data['parameters'].append(param_info)
|
271
|
-
else:
|
272
|
-
# 参数信息(对于其他类型的关键字)
|
273
|
-
parameters = keyword_info.get('parameters', [])
|
274
|
-
for param in parameters:
|
275
|
-
param_data = {
|
276
|
-
'name': getattr(param, 'name', str(param)),
|
277
|
-
'mapping': getattr(param, 'mapping', ''),
|
278
|
-
'description': getattr(param, 'description', '')
|
279
|
-
}
|
280
|
-
|
281
|
-
# 添加默认值信息
|
282
|
-
param_default = getattr(param, 'default', None)
|
283
|
-
if param_default is not None:
|
284
|
-
param_data['default'] = param_default
|
285
|
-
|
286
|
-
keyword_data['parameters'].append(param_data)
|
287
|
-
|
288
|
-
# 函数文档
|
289
|
-
func = keyword_info.get('func')
|
290
|
-
if func and hasattr(func, '__doc__') and func.__doc__:
|
291
|
-
keyword_data['documentation'] = func.__doc__.strip()
|
292
|
-
|
293
|
-
return keyword_data
|
294
|
-
|
295
|
-
|
296
|
-
def generate_html_report(keywords_data, output_file):
|
297
|
-
"""生成HTML格式的关键字报告"""
|
298
|
-
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
299
|
-
import os
|
300
|
-
|
301
|
-
# 准备数据
|
302
|
-
summary = keywords_data['summary']
|
303
|
-
keywords = keywords_data['keywords']
|
304
|
-
|
305
|
-
# 按类别分组
|
306
|
-
categories = {}
|
307
|
-
for keyword in keywords:
|
308
|
-
category = keyword['category']
|
309
|
-
if category not in categories:
|
310
|
-
categories[category] = []
|
311
|
-
categories[category].append(keyword)
|
312
|
-
|
313
|
-
# 按来源分组(用于更详细的分组视图)
|
314
|
-
source_groups = {}
|
315
|
-
for keyword in keywords:
|
316
|
-
source_info = keyword.get('source_info', {})
|
317
|
-
category = keyword['category']
|
318
|
-
source_name = source_info.get('name', '未知来源')
|
319
|
-
|
320
|
-
# 构建分组键
|
321
|
-
if category == 'plugin':
|
322
|
-
group_key = f"插件 - {source_name}"
|
323
|
-
elif category == 'builtin':
|
324
|
-
group_key = "内置关键字"
|
325
|
-
elif category == 'project_custom':
|
326
|
-
group_key = f"项目自定义 - {keyword.get('file_location', source_name)}"
|
327
|
-
elif category == 'remote':
|
328
|
-
group_key = f"远程 - {source_name}"
|
329
|
-
else:
|
330
|
-
group_key = f"自定义 - {source_name}"
|
331
|
-
|
332
|
-
if group_key not in source_groups:
|
333
|
-
source_groups[group_key] = []
|
334
|
-
source_groups[group_key].append(keyword)
|
335
|
-
|
336
|
-
# 按位置分组(用于全部关键字视图,保持向后兼容)
|
337
|
-
location_groups = {}
|
338
|
-
for keyword in keywords:
|
339
|
-
# 优先使用file_location,然后使用source_info中的name
|
340
|
-
location = keyword.get('file_location')
|
341
|
-
if not location:
|
342
|
-
source_info = keyword.get('source_info', {})
|
343
|
-
location = source_info.get('name', '内置/插件')
|
344
|
-
|
345
|
-
if location not in location_groups:
|
346
|
-
location_groups[location] = []
|
347
|
-
location_groups[location].append(keyword)
|
348
|
-
|
349
|
-
# 类别名称映射
|
350
|
-
category_names = {
|
351
|
-
'builtin': '内置',
|
352
|
-
'plugin': '插件',
|
353
|
-
'custom': '自定义',
|
354
|
-
'project_custom': '项目自定义',
|
355
|
-
'remote': '远程'
|
356
|
-
}
|
357
|
-
|
358
|
-
# 设置Jinja2环境
|
359
|
-
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
|
360
|
-
|
361
|
-
env = Environment(
|
362
|
-
loader=FileSystemLoader(template_dir),
|
363
|
-
autoescape=select_autoescape(['html', 'xml'])
|
364
|
-
)
|
365
|
-
|
366
|
-
# 加载模板
|
367
|
-
template = env.get_template('keywords_report.html')
|
368
|
-
|
369
|
-
# 渲染模板
|
370
|
-
html_content = template.render(
|
371
|
-
summary=summary,
|
372
|
-
keywords=keywords,
|
373
|
-
categories=categories,
|
374
|
-
source_groups=source_groups,
|
375
|
-
location_groups=location_groups,
|
376
|
-
category_names=category_names
|
377
|
-
)
|
378
|
-
|
379
|
-
# 写入文件
|
380
|
-
with open(output_file, 'w', encoding='utf-8') as f:
|
381
|
-
f.write(html_content)
|
382
|
-
|
383
|
-
print(f"HTML报告已生成: {output_file}")
|
384
|
-
|
385
|
-
|
386
139
|
def list_keywords(output_format='json', name_filter=None,
|
387
140
|
category_filter='all', output_file=None,
|
388
141
|
include_remote=False):
|
389
|
-
"""
|
390
|
-
import json
|
391
|
-
|
142
|
+
"""罗列所有关键字信息(简化版,调用统一的工具函数)"""
|
392
143
|
print("正在加载关键字...")
|
393
|
-
project_custom_keywords = load_all_keywords(include_remote=include_remote)
|
394
|
-
|
395
|
-
# 获取所有注册的关键字
|
396
|
-
all_keywords = keyword_manager._keywords
|
397
|
-
|
398
|
-
if not all_keywords:
|
399
|
-
print("未发现任何关键字")
|
400
|
-
return
|
401
|
-
|
402
|
-
# 过滤关键字
|
403
|
-
filtered_keywords = {}
|
404
144
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
# 类别过滤
|
415
|
-
if category_filter != 'all':
|
416
|
-
keyword_category = categorize_keyword(
|
417
|
-
name, info, project_custom_keywords
|
418
|
-
)
|
419
|
-
if keyword_category != category_filter:
|
420
|
-
continue
|
421
|
-
|
422
|
-
filtered_keywords[name] = info
|
423
|
-
|
424
|
-
if not filtered_keywords:
|
425
|
-
if name_filter:
|
426
|
-
print(f"未找到包含 '{name_filter}' 的关键字")
|
427
|
-
else:
|
428
|
-
print(f"未找到 {category_filter} 类别的关键字")
|
429
|
-
return
|
430
|
-
|
431
|
-
# 输出统计信息
|
432
|
-
total_count = len(filtered_keywords)
|
433
|
-
category_counts = {}
|
434
|
-
source_counts = {}
|
435
|
-
|
436
|
-
for name, info in filtered_keywords.items():
|
437
|
-
cat = categorize_keyword(name, info, project_custom_keywords)
|
438
|
-
category_counts[cat] = category_counts.get(cat, 0) + 1
|
439
|
-
|
440
|
-
# 统计各来源的关键字数量
|
441
|
-
source_info = get_keyword_source_info(info)
|
442
|
-
source_name = source_info['name']
|
443
|
-
if cat == 'project_custom' and project_custom_keywords:
|
444
|
-
custom_info = project_custom_keywords[name]
|
445
|
-
source_name = custom_info['file']
|
446
|
-
|
447
|
-
source_key = f"{cat}:{source_name}"
|
448
|
-
source_counts[source_key] = source_counts.get(source_key, 0) + 1
|
449
|
-
|
450
|
-
if output_format == 'text':
|
451
|
-
print(f"\n找到 {total_count} 个关键字:")
|
452
|
-
for cat, count in category_counts.items():
|
453
|
-
cat_names = {
|
454
|
-
'builtin': '内置', 'plugin': '插件', 'custom': '自定义',
|
455
|
-
'project_custom': '项目自定义', 'remote': '远程'
|
456
|
-
}
|
457
|
-
print(f" {cat_names.get(cat, cat)}: {count} 个")
|
458
|
-
print("-" * 60)
|
459
|
-
|
460
|
-
# 按类别和来源分组显示
|
461
|
-
grouped = group_keywords_by_source(
|
462
|
-
filtered_keywords, project_custom_keywords
|
145
|
+
# 使用统一的工具函数
|
146
|
+
try:
|
147
|
+
utils_list_keywords(
|
148
|
+
output_format=output_format,
|
149
|
+
name_filter=name_filter,
|
150
|
+
category_filter=category_filter,
|
151
|
+
include_remote=include_remote,
|
152
|
+
output_file=output_file,
|
153
|
+
print_summary=True
|
463
154
|
)
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
]:
|
468
|
-
if category not in grouped or not grouped[category]:
|
469
|
-
continue
|
470
|
-
|
471
|
-
cat_names = {
|
472
|
-
'builtin': '内置关键字',
|
473
|
-
'plugin': '插件关键字',
|
474
|
-
'custom': '自定义关键字',
|
475
|
-
'project_custom': '项目自定义关键字',
|
476
|
-
'remote': '远程关键字'
|
477
|
-
}
|
478
|
-
print(f"\n=== {cat_names[category]} ===")
|
479
|
-
|
480
|
-
for source_name, keyword_list in grouped[category].items():
|
481
|
-
if len(grouped[category]) > 1: # 如果有多个来源,显示来源名
|
482
|
-
print(f"\n--- {source_name} ---")
|
483
|
-
|
484
|
-
for keyword_data in keyword_list:
|
485
|
-
name = keyword_data['name']
|
486
|
-
info = keyword_data['info']
|
487
|
-
print()
|
488
|
-
print(format_keyword_info_text(
|
489
|
-
name, info, show_category=False,
|
490
|
-
project_custom_keywords=project_custom_keywords
|
491
|
-
))
|
492
|
-
|
493
|
-
elif output_format == 'json':
|
494
|
-
keywords_data = {
|
495
|
-
'summary': {
|
496
|
-
'total_count': total_count,
|
497
|
-
'category_counts': category_counts,
|
498
|
-
'source_counts': source_counts
|
499
|
-
},
|
500
|
-
'keywords': []
|
501
|
-
}
|
502
|
-
|
503
|
-
for name in sorted(filtered_keywords.keys()):
|
504
|
-
info = filtered_keywords[name]
|
505
|
-
keyword_data = format_keyword_info_json(
|
506
|
-
name, info, project_custom_keywords
|
507
|
-
)
|
508
|
-
keywords_data['keywords'].append(keyword_data)
|
509
|
-
|
510
|
-
json_output = json.dumps(keywords_data, ensure_ascii=False, indent=2)
|
511
|
-
|
512
|
-
# 确定输出文件名
|
513
|
-
if output_file is None:
|
514
|
-
output_file = 'keywords.json'
|
515
|
-
|
516
|
-
# 写入到文件
|
517
|
-
try:
|
518
|
-
with open(output_file, 'w', encoding='utf-8') as f:
|
519
|
-
f.write(json_output)
|
520
|
-
print(f"关键字信息已保存到文件: {output_file}")
|
521
|
-
print(f"共 {total_count} 个关键字")
|
522
|
-
for cat, count in category_counts.items():
|
523
|
-
cat_names = {
|
524
|
-
'builtin': '内置', 'plugin': '插件', 'custom': '自定义',
|
525
|
-
'project_custom': '项目自定义', 'remote': '远程'
|
526
|
-
}
|
527
|
-
print(f" {cat_names.get(cat, cat)}: {count} 个")
|
528
|
-
except Exception as e:
|
529
|
-
print(f"保存文件失败: {e}")
|
530
|
-
# 如果写入文件失败,则回退到打印
|
531
|
-
print(json_output)
|
532
|
-
|
533
|
-
elif output_format == 'html':
|
534
|
-
keywords_data = {
|
535
|
-
'summary': {
|
536
|
-
'total_count': total_count,
|
537
|
-
'category_counts': category_counts,
|
538
|
-
'source_counts': source_counts
|
539
|
-
},
|
540
|
-
'keywords': []
|
541
|
-
}
|
542
|
-
|
543
|
-
for name in sorted(filtered_keywords.keys()):
|
544
|
-
info = filtered_keywords[name]
|
545
|
-
keyword_data = format_keyword_info_json(
|
546
|
-
name, info, project_custom_keywords
|
547
|
-
)
|
548
|
-
keywords_data['keywords'].append(keyword_data)
|
549
|
-
|
550
|
-
# 确定输出文件名
|
551
|
-
if output_file is None:
|
552
|
-
output_file = 'keywords.html'
|
553
|
-
|
554
|
-
# 生成HTML报告
|
555
|
-
try:
|
556
|
-
generate_html_report(keywords_data, output_file)
|
557
|
-
print(f"共 {total_count} 个关键字")
|
558
|
-
for cat, count in category_counts.items():
|
559
|
-
cat_names = {
|
560
|
-
'builtin': '内置', 'plugin': '插件', 'custom': '自定义',
|
561
|
-
'project_custom': '项目自定义', 'remote': '远程'
|
562
|
-
}
|
563
|
-
print(f" {cat_names.get(cat, cat)}: {count} 个")
|
564
|
-
except Exception as e:
|
565
|
-
print(f"生成HTML报告失败: {e}")
|
566
|
-
raise
|
155
|
+
except Exception as e:
|
156
|
+
print(f"列出关键字失败: {e}")
|
157
|
+
raise
|
567
158
|
|
568
159
|
|
569
160
|
def load_yaml_variables(args):
|
@@ -776,8 +367,5 @@ def main_list_keywords():
|
|
776
367
|
)
|
777
368
|
|
778
369
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
370
|
if __name__ == '__main__':
|
783
371
|
main()
|