pytest-dsl 0.11.0__py3-none-any.whl → 0.12.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 CHANGED
@@ -69,7 +69,7 @@ def parse_args():
69
69
  )
70
70
  list_parser.add_argument(
71
71
  '--output', '-o', type=str, default=None,
72
- help='输出文件路径(仅对 json 格式有效,默认为 keywords.json)'
72
+ help='输出文件路径(json格式默认为keywords.json,html格式默认为keywords.html)'
73
73
  )
74
74
  list_parser.add_argument(
75
75
  '--filter', type=str, default=None,
@@ -77,7 +77,10 @@ def parse_args():
77
77
  )
78
78
  list_parser.add_argument(
79
79
  '--category',
80
- choices=['builtin', 'plugin', 'custom', 'project_custom', 'remote', 'all'],
80
+ choices=[
81
+ 'builtin', 'plugin', 'custom',
82
+ 'project_custom', 'remote', 'all'
83
+ ],
81
84
  default='all',
82
85
  help='关键字类别:builtin(内置)、plugin(插件)、custom(自定义)、'
83
86
  'project_custom(项目自定义)、remote(远程)、all(全部,默认)'
@@ -105,7 +108,8 @@ def parse_args():
105
108
  parser.add_argument(
106
109
  '--category',
107
110
  choices=[
108
- 'builtin', 'plugin', 'custom', 'project_custom', 'remote', 'all'
111
+ 'builtin', 'plugin', 'custom',
112
+ 'project_custom', 'remote', 'all'
109
113
  ],
110
114
  default='all'
111
115
  )
@@ -136,7 +140,7 @@ def parse_args():
136
140
 
137
141
  def load_all_keywords(include_remote=False):
138
142
  """加载所有可用的关键字
139
-
143
+
140
144
  Args:
141
145
  include_remote: 是否包含远程关键字,默认为False
142
146
  """
@@ -152,28 +156,28 @@ def load_all_keywords(include_remote=False):
152
156
 
153
157
  # 扫描本地关键字
154
158
  scan_local_keywords()
155
-
159
+
156
160
  # 扫描项目中的自定义关键字(.resource文件中定义的)
157
161
  project_custom_keywords = scan_project_custom_keywords()
158
162
  if project_custom_keywords:
159
163
  print(f"发现 {len(project_custom_keywords)} 个项目自定义关键字")
160
-
164
+
161
165
  # 加载.resource文件中的关键字到关键字管理器
162
166
  from pytest_dsl.core.custom_keyword_manager import (
163
167
  custom_keyword_manager
164
168
  )
165
169
  from pathlib import Path
166
-
170
+
167
171
  project_root = Path(os.getcwd())
168
172
  resource_files = list(project_root.glob('**/*.resource'))
169
-
173
+
170
174
  for resource_file in resource_files:
171
175
  try:
172
176
  custom_keyword_manager.load_resource_file(str(resource_file))
173
177
  print(f"已加载资源文件: {resource_file}")
174
178
  except Exception as e:
175
179
  print(f"加载资源文件失败 {resource_file}: {e}")
176
-
180
+
177
181
  # 根据参数决定是否加载远程关键字
178
182
  if include_remote:
179
183
  print("正在扫描远程关键字...")
@@ -181,11 +185,11 @@ def load_all_keywords(include_remote=False):
181
185
  # 目前远程关键字是通过DSL文件中的@remote导入指令动态加载的
182
186
  else:
183
187
  print("跳过远程关键字扫描")
184
-
188
+
185
189
  return project_custom_keywords
186
190
 
187
191
 
188
- def categorize_keyword(keyword_name, keyword_info,
192
+ def categorize_keyword(keyword_name, keyword_info,
189
193
  project_custom_keywords=None):
190
194
  """判断关键字的类别"""
191
195
  # 优先使用存储的来源信息
@@ -199,11 +203,11 @@ def categorize_keyword(keyword_name, keyword_info,
199
203
  return 'custom'
200
204
  elif source_type == 'project_custom':
201
205
  return 'project_custom'
202
-
206
+
203
207
  # 向后兼容:使用原有的判断逻辑
204
208
  if keyword_info.get('remote', False):
205
209
  return 'remote'
206
-
210
+
207
211
  # 检查是否是项目自定义关键字(DSL文件中定义的)
208
212
  if project_custom_keywords and keyword_name in project_custom_keywords:
209
213
  return 'project_custom'
@@ -222,7 +226,7 @@ def get_keyword_source_info(keyword_info):
222
226
  """获取关键字的详细来源信息"""
223
227
  source_type = keyword_info.get('source_type', 'unknown')
224
228
  source_name = keyword_info.get('source_name', '未知')
225
-
229
+
226
230
  return {
227
231
  'type': source_type,
228
232
  'name': source_name,
@@ -234,7 +238,7 @@ def get_keyword_source_info(keyword_info):
234
238
 
235
239
  def group_keywords_by_source(keywords_dict, project_custom_keywords=None):
236
240
  """按来源分组关键字
237
-
241
+
238
242
  Returns:
239
243
  dict: 格式为 {source_group: {source_name: [keywords]}}
240
244
  """
@@ -245,33 +249,33 @@ def group_keywords_by_source(keywords_dict, project_custom_keywords=None):
245
249
  'project_custom': {},
246
250
  'remote': {}
247
251
  }
248
-
252
+
249
253
  for keyword_name, keyword_info in keywords_dict.items():
250
254
  category = categorize_keyword(
251
255
  keyword_name, keyword_info, project_custom_keywords
252
256
  )
253
257
  source_info = get_keyword_source_info(keyword_info)
254
-
258
+
255
259
  # 特殊处理项目自定义关键字
256
260
  if category == 'project_custom' and project_custom_keywords:
257
261
  custom_info = project_custom_keywords[keyword_name]
258
262
  source_name = custom_info['file']
259
263
  else:
260
264
  source_name = source_info['name']
261
-
265
+
262
266
  if source_name not in groups[category]:
263
267
  groups[category][source_name] = []
264
-
268
+
265
269
  groups[category][source_name].append({
266
270
  'name': keyword_name,
267
271
  'info': keyword_info,
268
272
  'source_info': source_info
269
273
  })
270
-
274
+
271
275
  return groups
272
276
 
273
277
 
274
- def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
278
+ def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
275
279
  project_custom_keywords=None):
276
280
  """格式化关键字信息为文本格式"""
277
281
  lines = []
@@ -281,9 +285,9 @@ def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
281
285
  keyword_name, keyword_info, project_custom_keywords
282
286
  )
283
287
  category_names = {
284
- 'builtin': '内置',
285
- 'custom': '自定义',
286
- 'project_custom': '项目自定义',
288
+ 'builtin': '内置',
289
+ 'custom': '自定义',
290
+ 'project_custom': '项目自定义',
287
291
  'remote': '远程'
288
292
  }
289
293
 
@@ -304,7 +308,7 @@ def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
304
308
  if category == 'project_custom' and project_custom_keywords:
305
309
  custom_info = project_custom_keywords[keyword_name]
306
310
  lines.append(f" 文件位置: {custom_info['file']}")
307
-
311
+
308
312
  # 对于项目自定义关键字,使用从AST中提取的参数信息
309
313
  custom_parameters = custom_info.get('parameters', [])
310
314
  if custom_parameters:
@@ -321,13 +325,13 @@ def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
321
325
  param_parts.append(f"{param_name} ({param_mapping})")
322
326
  else:
323
327
  param_parts.append(param_name)
324
-
328
+
325
329
  param_parts.append(f": {param_desc}")
326
-
330
+
327
331
  # 添加默认值信息
328
332
  if param_default is not None:
329
333
  param_parts.append(f" (默认值: {param_default})")
330
-
334
+
331
335
  lines.append(f" {''.join(param_parts)}")
332
336
  else:
333
337
  lines.append(" 参数: 无")
@@ -348,13 +352,13 @@ def format_keyword_info_text(keyword_name, keyword_info, show_category=True,
348
352
  param_info.append(f"{param_name} ({param_mapping})")
349
353
  else:
350
354
  param_info.append(param_name)
351
-
355
+
352
356
  param_info.append(f": {param_desc}")
353
-
357
+
354
358
  # 添加默认值信息
355
359
  if param_default is not None:
356
360
  param_info.append(f" (默认值: {param_default})")
357
-
361
+
358
362
  lines.append(f" {''.join(param_info)}")
359
363
  else:
360
364
  lines.append(" 参数: 无")
@@ -393,7 +397,7 @@ def format_keyword_info_json(keyword_name, keyword_info,
393
397
  if category == 'project_custom' and project_custom_keywords:
394
398
  custom_info = project_custom_keywords[keyword_name]
395
399
  keyword_data['file_location'] = custom_info['file']
396
-
400
+
397
401
  # 对于项目自定义关键字,使用从AST中提取的参数信息
398
402
  for param_info in custom_info.get('parameters', []):
399
403
  keyword_data['parameters'].append(param_info)
@@ -406,12 +410,12 @@ def format_keyword_info_json(keyword_name, keyword_info,
406
410
  'mapping': getattr(param, 'mapping', ''),
407
411
  'description': getattr(param, 'description', '')
408
412
  }
409
-
413
+
410
414
  # 添加默认值信息
411
415
  param_default = getattr(param, 'default', None)
412
416
  if param_default is not None:
413
417
  param_data['default'] = param_default
414
-
418
+
415
419
  keyword_data['parameters'].append(param_data)
416
420
 
417
421
  # 函数文档
@@ -426,11 +430,11 @@ def generate_html_report(keywords_data, output_file):
426
430
  """生成HTML格式的关键字报告"""
427
431
  from jinja2 import Environment, FileSystemLoader, select_autoescape
428
432
  import os
429
-
433
+
430
434
  # 准备数据
431
435
  summary = keywords_data['summary']
432
436
  keywords = keywords_data['keywords']
433
-
437
+
434
438
  # 按类别分组
435
439
  categories = {}
436
440
  for keyword in keywords:
@@ -438,14 +442,14 @@ def generate_html_report(keywords_data, output_file):
438
442
  if category not in categories:
439
443
  categories[category] = []
440
444
  categories[category].append(keyword)
441
-
445
+
442
446
  # 按来源分组(用于更详细的分组视图)
443
447
  source_groups = {}
444
448
  for keyword in keywords:
445
449
  source_info = keyword.get('source_info', {})
446
450
  category = keyword['category']
447
451
  source_name = source_info.get('name', '未知来源')
448
-
452
+
449
453
  # 构建分组键
450
454
  if category == 'plugin':
451
455
  group_key = f"插件 - {source_name}"
@@ -457,11 +461,11 @@ def generate_html_report(keywords_data, output_file):
457
461
  group_key = f"远程 - {source_name}"
458
462
  else:
459
463
  group_key = f"自定义 - {source_name}"
460
-
464
+
461
465
  if group_key not in source_groups:
462
466
  source_groups[group_key] = []
463
467
  source_groups[group_key].append(keyword)
464
-
468
+
465
469
  # 按位置分组(用于全部关键字视图,保持向后兼容)
466
470
  location_groups = {}
467
471
  for keyword in keywords:
@@ -470,11 +474,11 @@ def generate_html_report(keywords_data, output_file):
470
474
  if not location:
471
475
  source_info = keyword.get('source_info', {})
472
476
  location = source_info.get('name', '内置/插件')
473
-
477
+
474
478
  if location not in location_groups:
475
479
  location_groups[location] = []
476
480
  location_groups[location].append(keyword)
477
-
481
+
478
482
  # 类别名称映射
479
483
  category_names = {
480
484
  'builtin': '内置',
@@ -483,18 +487,18 @@ def generate_html_report(keywords_data, output_file):
483
487
  'project_custom': '项目自定义',
484
488
  'remote': '远程'
485
489
  }
486
-
490
+
487
491
  # 设置Jinja2环境
488
492
  template_dir = os.path.join(os.path.dirname(__file__), 'templates')
489
-
493
+
490
494
  env = Environment(
491
495
  loader=FileSystemLoader(template_dir),
492
496
  autoescape=select_autoescape(['html', 'xml'])
493
497
  )
494
-
498
+
495
499
  # 加载模板
496
500
  template = env.get_template('keywords_report.html')
497
-
501
+
498
502
  # 渲染模板
499
503
  html_content = template.render(
500
504
  summary=summary,
@@ -504,16 +508,16 @@ def generate_html_report(keywords_data, output_file):
504
508
  location_groups=location_groups,
505
509
  category_names=category_names
506
510
  )
507
-
511
+
508
512
  # 写入文件
509
513
  with open(output_file, 'w', encoding='utf-8') as f:
510
514
  f.write(html_content)
511
-
515
+
512
516
  print(f"HTML报告已生成: {output_file}")
513
517
 
514
518
 
515
519
  def list_keywords(output_format='json', name_filter=None,
516
- category_filter='all', output_file=None,
520
+ category_filter='all', output_file=None,
517
521
  include_remote=False):
518
522
  """罗列所有关键字信息"""
519
523
  import json
@@ -561,18 +565,18 @@ def list_keywords(output_format='json', name_filter=None,
561
565
  total_count = len(filtered_keywords)
562
566
  category_counts = {}
563
567
  source_counts = {}
564
-
568
+
565
569
  for name, info in filtered_keywords.items():
566
570
  cat = categorize_keyword(name, info, project_custom_keywords)
567
571
  category_counts[cat] = category_counts.get(cat, 0) + 1
568
-
572
+
569
573
  # 统计各来源的关键字数量
570
574
  source_info = get_keyword_source_info(info)
571
575
  source_name = source_info['name']
572
576
  if cat == 'project_custom' and project_custom_keywords:
573
577
  custom_info = project_custom_keywords[name]
574
578
  source_name = custom_info['file']
575
-
579
+
576
580
  source_key = f"{cat}:{source_name}"
577
581
  source_counts[source_key] = source_counts.get(source_key, 0) + 1
578
582
 
@@ -590,13 +594,13 @@ def list_keywords(output_format='json', name_filter=None,
590
594
  grouped = group_keywords_by_source(
591
595
  filtered_keywords, project_custom_keywords
592
596
  )
593
-
597
+
594
598
  for category in [
595
599
  'builtin', 'plugin', 'custom', 'project_custom', 'remote'
596
600
  ]:
597
601
  if category not in grouped or not grouped[category]:
598
602
  continue
599
-
603
+
600
604
  cat_names = {
601
605
  'builtin': '内置关键字',
602
606
  'plugin': '插件关键字',
@@ -605,11 +609,11 @@ def list_keywords(output_format='json', name_filter=None,
605
609
  'remote': '远程关键字'
606
610
  }
607
611
  print(f"\n=== {cat_names[category]} ===")
608
-
612
+
609
613
  for source_name, keyword_list in grouped[category].items():
610
614
  if len(grouped[category]) > 1: # 如果有多个来源,显示来源名
611
615
  print(f"\n--- {source_name} ---")
612
-
616
+
613
617
  for keyword_data in keyword_list:
614
618
  name = keyword_data['name']
615
619
  info = keyword_data['info']
@@ -637,11 +641,11 @@ def list_keywords(output_format='json', name_filter=None,
637
641
  keywords_data['keywords'].append(keyword_data)
638
642
 
639
643
  json_output = json.dumps(keywords_data, ensure_ascii=False, indent=2)
640
-
644
+
641
645
  # 确定输出文件名
642
646
  if output_file is None:
643
647
  output_file = 'keywords.json'
644
-
648
+
645
649
  # 写入到文件
646
650
  try:
647
651
  with open(output_file, 'w', encoding='utf-8') as f:
@@ -679,7 +683,7 @@ def list_keywords(output_format='json', name_filter=None,
679
683
  # 确定输出文件名
680
684
  if output_file is None:
681
685
  output_file = 'keywords.html'
682
-
686
+
683
687
  # 生成HTML报告
684
688
  try:
685
689
  generate_html_report(keywords_data, output_file)
@@ -850,7 +854,9 @@ def main_list_keywords():
850
854
  )
851
855
  parser.add_argument(
852
856
  '--category',
853
- choices=['builtin', 'plugin', 'custom', 'project_custom', 'remote', 'all'],
857
+ choices=[
858
+ 'builtin', 'plugin', 'custom', 'project_custom', 'remote', 'all'
859
+ ],
854
860
  default='all',
855
861
  help='关键字类别:builtin(内置)、plugin(插件)、custom(自定义)、'
856
862
  'project_custom(项目自定义)、remote(远程)、all(全部,默认)'
@@ -873,103 +879,103 @@ def main_list_keywords():
873
879
 
874
880
  def scan_project_custom_keywords(project_root=None):
875
881
  """扫描项目中.resource文件中的自定义关键字
876
-
882
+
877
883
  Args:
878
884
  project_root: 项目根目录,默认为当前工作目录
879
-
885
+
880
886
  Returns:
881
887
  dict: 自定义关键字信息,格式为
882
888
  {keyword_name: {'file': file_path, 'node': ast_node}}
883
889
  """
884
890
  if project_root is None:
885
891
  project_root = os.getcwd()
886
-
892
+
887
893
  project_root = Path(project_root)
888
894
  custom_keywords = {}
889
-
895
+
890
896
  # 查找所有.resource文件
891
897
  resource_files = list(project_root.glob('**/*.resource'))
892
-
898
+
893
899
  if not resource_files:
894
900
  return custom_keywords
895
-
901
+
896
902
  lexer = get_lexer()
897
903
  parser = get_parser()
898
-
904
+
899
905
  for file_path in resource_files:
900
906
  try:
901
907
  # 读取并解析文件
902
908
  content = read_file(str(file_path))
903
909
  ast = parser.parse(content, lexer=lexer)
904
-
910
+
905
911
  # 查找自定义关键字定义
906
912
  keywords_in_file = extract_custom_keywords_from_ast(
907
913
  ast, str(file_path)
908
914
  )
909
915
  custom_keywords.update(keywords_in_file)
910
-
916
+
911
917
  except Exception as e:
912
918
  print(f"解析资源文件 {file_path} 时出错: {e}")
913
-
919
+
914
920
  return custom_keywords
915
921
 
916
922
 
917
923
  def extract_custom_keywords_from_ast(ast, file_path):
918
924
  """从AST中提取自定义关键字定义
919
-
925
+
920
926
  Args:
921
927
  ast: 抽象语法树
922
928
  file_path: 文件路径
923
-
929
+
924
930
  Returns:
925
931
  dict: 自定义关键字信息
926
932
  """
927
933
  custom_keywords = {}
928
-
934
+
929
935
  if ast.type != 'Start' or len(ast.children) < 2:
930
936
  return custom_keywords
931
-
937
+
932
938
  # 遍历语句节点
933
939
  statements_node = ast.children[1]
934
940
  if statements_node.type != 'Statements':
935
941
  return custom_keywords
936
-
942
+
937
943
  for node in statements_node.children:
938
944
  # 支持两种格式:CustomKeyword(旧格式)和Function(新格式)
939
945
  if node.type in ['CustomKeyword', 'Function']:
940
946
  keyword_name = node.value
941
-
947
+
942
948
  # 提取参数信息
943
949
  params_node = node.children[0] if node.children else None
944
950
  parameters = []
945
-
951
+
946
952
  if params_node:
947
953
  for param in params_node:
948
954
  param_name = param.value
949
955
  param_default = None
950
-
956
+
951
957
  # 检查是否有默认值
952
958
  if param.children and param.children[0]:
953
959
  param_default = param.children[0].value
954
-
960
+
955
961
  param_info = {
956
962
  'name': param_name,
957
963
  'mapping': param_name,
958
964
  'description': f'自定义关键字参数 {param_name}'
959
965
  }
960
-
966
+
961
967
  if param_default is not None:
962
968
  param_info['default'] = param_default
963
-
969
+
964
970
  parameters.append(param_info)
965
-
971
+
966
972
  custom_keywords[keyword_name] = {
967
973
  'file': file_path,
968
974
  'node': node,
969
975
  'type': 'project_custom',
970
976
  'parameters': parameters
971
977
  }
972
-
978
+
973
979
  return custom_keywords
974
980
 
975
981