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 +89 -83
- pytest_dsl/core/http_client.py +93 -80
- pytest_dsl/keywords/http_keywords.py +90 -34
- pytest_dsl/keywords/system_keywords.py +420 -1
- pytest_dsl/remote/keyword_client.py +30 -17
- pytest_dsl/remote/keyword_server.py +18 -1
- pytest_dsl/remote/variable_bridge.py +41 -8
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/METADATA +3 -3
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/RECORD +13 -13
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/WHEEL +0 -0
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/entry_points.txt +0 -0
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/licenses/LICENSE +0 -0
- {pytest_dsl-0.11.0.dist-info → pytest_dsl-0.12.0.dist-info}/top_level.txt +0 -0
@@ -185,7 +185,8 @@ def generate_random_number(**kwargs):
|
|
185
185
|
|
186
186
|
@keyword_manager.register('字符串操作', [
|
187
187
|
{'name': '操作', 'mapping': 'operation',
|
188
|
-
'description': '操作类型:拼接(concat)、替换(replace)、分割(split)
|
188
|
+
'description': '操作类型:拼接(concat)、替换(replace)、分割(split)、'
|
189
|
+
'大写(upper)、小写(lower)、去空格(strip)',
|
189
190
|
'default': 'strip'},
|
190
191
|
{'name': '字符串', 'mapping': 'string', 'description': '要操作的字符串'},
|
191
192
|
{'name': '参数1', 'mapping': 'param1',
|
@@ -366,3 +367,421 @@ def execute_command(**kwargs):
|
|
366
367
|
'stdout': '',
|
367
368
|
'stderr': str(e)
|
368
369
|
}
|
370
|
+
|
371
|
+
|
372
|
+
@keyword_manager.register('求和', [
|
373
|
+
{'name': '数据', 'mapping': 'data', 'description': '要求和的数字列表或可迭代对象'},
|
374
|
+
{'name': '起始值', 'mapping': 'start', 'description': '求和的起始值', 'default': 0}
|
375
|
+
])
|
376
|
+
def sum_values(**kwargs):
|
377
|
+
"""计算数字列表的总和
|
378
|
+
|
379
|
+
Args:
|
380
|
+
data: 要求和的数字列表
|
381
|
+
start: 求和的起始值,默认为0
|
382
|
+
|
383
|
+
Returns:
|
384
|
+
数字总和
|
385
|
+
"""
|
386
|
+
data = kwargs.get('data', [])
|
387
|
+
start = kwargs.get('start', 0)
|
388
|
+
|
389
|
+
# 确保data是可迭代的
|
390
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
391
|
+
raise ValueError("数据必须是可迭代的数字列表")
|
392
|
+
|
393
|
+
try:
|
394
|
+
result = sum(data, start)
|
395
|
+
|
396
|
+
with allure.step(f"求和计算: 数据长度={len(data)}, 起始值={start}"):
|
397
|
+
allure.attach(
|
398
|
+
f"输入数据: {data}\n起始值: {start}\n结果: {result}",
|
399
|
+
name="求和结果",
|
400
|
+
attachment_type=allure.attachment_type.TEXT
|
401
|
+
)
|
402
|
+
|
403
|
+
return result
|
404
|
+
except Exception as e:
|
405
|
+
allure.attach(
|
406
|
+
f"求和计算失败: {str(e)}",
|
407
|
+
name="求和错误",
|
408
|
+
attachment_type=allure.attachment_type.TEXT
|
409
|
+
)
|
410
|
+
raise
|
411
|
+
|
412
|
+
|
413
|
+
@keyword_manager.register('获取长度', [
|
414
|
+
{'name': '对象', 'mapping': 'obj', 'description': '要获取长度的对象(字符串、列表、字典等)'}
|
415
|
+
])
|
416
|
+
def get_length(**kwargs):
|
417
|
+
"""获取对象的长度
|
418
|
+
|
419
|
+
Args:
|
420
|
+
obj: 要获取长度的对象
|
421
|
+
|
422
|
+
Returns:
|
423
|
+
int: 对象的长度
|
424
|
+
"""
|
425
|
+
obj = kwargs.get('obj')
|
426
|
+
|
427
|
+
if obj is None:
|
428
|
+
return 0
|
429
|
+
|
430
|
+
try:
|
431
|
+
result = len(obj)
|
432
|
+
|
433
|
+
with allure.step(f"获取长度: 对象类型={type(obj).__name__}"):
|
434
|
+
allure.attach(
|
435
|
+
f"对象: {obj}\n类型: {type(obj).__name__}\n长度: {result}",
|
436
|
+
name="长度计算结果",
|
437
|
+
attachment_type=allure.attachment_type.TEXT
|
438
|
+
)
|
439
|
+
|
440
|
+
return result
|
441
|
+
except Exception as e:
|
442
|
+
allure.attach(
|
443
|
+
f"获取长度失败: {str(e)}",
|
444
|
+
name="长度计算错误",
|
445
|
+
attachment_type=allure.attachment_type.TEXT
|
446
|
+
)
|
447
|
+
raise
|
448
|
+
|
449
|
+
|
450
|
+
@keyword_manager.register('获取最大值', [
|
451
|
+
{'name': '数据', 'mapping': 'data', 'description': '要比较的数据列表或多个参数'},
|
452
|
+
{'name': '默认值', 'mapping': 'default',
|
453
|
+
'description': '当数据为空时的默认值', 'default': None}
|
454
|
+
])
|
455
|
+
def get_max_value(**kwargs):
|
456
|
+
"""获取最大值
|
457
|
+
|
458
|
+
Args:
|
459
|
+
data: 要比较的数据
|
460
|
+
default: 当数据为空时的默认值
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
最大值
|
464
|
+
"""
|
465
|
+
data = kwargs.get('data')
|
466
|
+
default = kwargs.get('default')
|
467
|
+
|
468
|
+
if data is None:
|
469
|
+
if default is not None:
|
470
|
+
return default
|
471
|
+
raise ValueError("数据不能为空")
|
472
|
+
|
473
|
+
try:
|
474
|
+
# 如果data不是可迭代的,将其转换为列表
|
475
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
476
|
+
data = [data]
|
477
|
+
|
478
|
+
if len(data) == 0:
|
479
|
+
if default is not None:
|
480
|
+
return default
|
481
|
+
raise ValueError("数据列表为空")
|
482
|
+
|
483
|
+
result = max(data)
|
484
|
+
|
485
|
+
with allure.step(f"获取最大值: 数据长度={len(data)}"):
|
486
|
+
allure.attach(
|
487
|
+
f"输入数据: {data}\n最大值: {result}",
|
488
|
+
name="最大值计算结果",
|
489
|
+
attachment_type=allure.attachment_type.TEXT
|
490
|
+
)
|
491
|
+
|
492
|
+
return result
|
493
|
+
except Exception as e:
|
494
|
+
allure.attach(
|
495
|
+
f"获取最大值失败: {str(e)}",
|
496
|
+
name="最大值计算错误",
|
497
|
+
attachment_type=allure.attachment_type.TEXT
|
498
|
+
)
|
499
|
+
raise
|
500
|
+
|
501
|
+
|
502
|
+
@keyword_manager.register('获取最小值', [
|
503
|
+
{'name': '数据', 'mapping': 'data', 'description': '要比较的数据列表或多个参数'},
|
504
|
+
{'name': '默认值', 'mapping': 'default',
|
505
|
+
'description': '当数据为空时的默认值', 'default': None}
|
506
|
+
])
|
507
|
+
def get_min_value(**kwargs):
|
508
|
+
"""获取最小值
|
509
|
+
|
510
|
+
Args:
|
511
|
+
data: 要比较的数据
|
512
|
+
default: 当数据为空时的默认值
|
513
|
+
|
514
|
+
Returns:
|
515
|
+
最小值
|
516
|
+
"""
|
517
|
+
data = kwargs.get('data')
|
518
|
+
default = kwargs.get('default')
|
519
|
+
|
520
|
+
if data is None:
|
521
|
+
if default is not None:
|
522
|
+
return default
|
523
|
+
raise ValueError("数据不能为空")
|
524
|
+
|
525
|
+
try:
|
526
|
+
# 如果data不是可迭代的,将其转换为列表
|
527
|
+
if not hasattr(data, '__iter__') or isinstance(data, str):
|
528
|
+
data = [data]
|
529
|
+
|
530
|
+
if len(data) == 0:
|
531
|
+
if default is not None:
|
532
|
+
return default
|
533
|
+
raise ValueError("数据列表为空")
|
534
|
+
|
535
|
+
result = min(data)
|
536
|
+
|
537
|
+
with allure.step(f"获取最小值: 数据长度={len(data)}"):
|
538
|
+
allure.attach(
|
539
|
+
f"输入数据: {data}\n最小值: {result}",
|
540
|
+
name="最小值计算结果",
|
541
|
+
attachment_type=allure.attachment_type.TEXT
|
542
|
+
)
|
543
|
+
|
544
|
+
return result
|
545
|
+
except Exception as e:
|
546
|
+
allure.attach(
|
547
|
+
f"获取最小值失败: {str(e)}",
|
548
|
+
name="最小值计算错误",
|
549
|
+
attachment_type=allure.attachment_type.TEXT
|
550
|
+
)
|
551
|
+
raise
|
552
|
+
|
553
|
+
|
554
|
+
@keyword_manager.register('绝对值', [
|
555
|
+
{'name': '数值', 'mapping': 'number', 'description': '要计算绝对值的数字'}
|
556
|
+
])
|
557
|
+
def get_absolute_value(**kwargs):
|
558
|
+
"""计算数字的绝对值
|
559
|
+
|
560
|
+
Args:
|
561
|
+
number: 要计算绝对值的数字
|
562
|
+
|
563
|
+
Returns:
|
564
|
+
数字的绝对值
|
565
|
+
"""
|
566
|
+
number = kwargs.get('number')
|
567
|
+
|
568
|
+
if number is None:
|
569
|
+
raise ValueError("数值不能为空")
|
570
|
+
|
571
|
+
try:
|
572
|
+
result = abs(number)
|
573
|
+
|
574
|
+
with allure.step(f"计算绝对值: {number}"):
|
575
|
+
allure.attach(
|
576
|
+
f"输入数值: {number}\n绝对值: {result}",
|
577
|
+
name="绝对值计算结果",
|
578
|
+
attachment_type=allure.attachment_type.TEXT
|
579
|
+
)
|
580
|
+
|
581
|
+
return result
|
582
|
+
except Exception as e:
|
583
|
+
allure.attach(
|
584
|
+
f"计算绝对值失败: {str(e)}",
|
585
|
+
name="绝对值计算错误",
|
586
|
+
attachment_type=allure.attachment_type.TEXT
|
587
|
+
)
|
588
|
+
raise
|
589
|
+
|
590
|
+
|
591
|
+
@keyword_manager.register('四舍五入', [
|
592
|
+
{'name': '数值', 'mapping': 'number', 'description': '要四舍五入的数字'},
|
593
|
+
{'name': '小数位数', 'mapping': 'ndigits',
|
594
|
+
'description': '保留的小数位数', 'default': 0}
|
595
|
+
])
|
596
|
+
def round_number(**kwargs):
|
597
|
+
"""对数字进行四舍五入
|
598
|
+
|
599
|
+
Args:
|
600
|
+
number: 要四舍五入的数字
|
601
|
+
ndigits: 保留的小数位数,默认为0(整数)
|
602
|
+
|
603
|
+
Returns:
|
604
|
+
四舍五入后的数字
|
605
|
+
"""
|
606
|
+
number = kwargs.get('number')
|
607
|
+
ndigits = kwargs.get('ndigits', 0)
|
608
|
+
|
609
|
+
if number is None:
|
610
|
+
raise ValueError("数值不能为空")
|
611
|
+
|
612
|
+
try:
|
613
|
+
if ndigits == 0:
|
614
|
+
result = round(number)
|
615
|
+
else:
|
616
|
+
result = round(number, int(ndigits))
|
617
|
+
|
618
|
+
with allure.step(f"四舍五入: {number} -> {ndigits}位小数"):
|
619
|
+
allure.attach(
|
620
|
+
f"输入数值: {number}\n小数位数: {ndigits}\n结果: {result}",
|
621
|
+
name="四舍五入结果",
|
622
|
+
attachment_type=allure.attachment_type.TEXT
|
623
|
+
)
|
624
|
+
|
625
|
+
return result
|
626
|
+
except Exception as e:
|
627
|
+
allure.attach(
|
628
|
+
f"四舍五入失败: {str(e)}",
|
629
|
+
name="四舍五入错误",
|
630
|
+
attachment_type=allure.attachment_type.TEXT
|
631
|
+
)
|
632
|
+
raise
|
633
|
+
|
634
|
+
|
635
|
+
@keyword_manager.register('转换为字符串', [
|
636
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为字符串的值'}
|
637
|
+
])
|
638
|
+
def convert_to_string(**kwargs):
|
639
|
+
"""将值转换为字符串
|
640
|
+
|
641
|
+
Args:
|
642
|
+
value: 要转换的值
|
643
|
+
|
644
|
+
Returns:
|
645
|
+
str: 转换后的字符串
|
646
|
+
"""
|
647
|
+
value = kwargs.get('value')
|
648
|
+
|
649
|
+
try:
|
650
|
+
result = str(value)
|
651
|
+
|
652
|
+
with allure.step(f"转换为字符串: {type(value).__name__} -> str"):
|
653
|
+
allure.attach(
|
654
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
655
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
656
|
+
name="字符串转换结果",
|
657
|
+
attachment_type=allure.attachment_type.TEXT
|
658
|
+
)
|
659
|
+
|
660
|
+
return result
|
661
|
+
except Exception as e:
|
662
|
+
allure.attach(
|
663
|
+
f"转换为字符串失败: {str(e)}",
|
664
|
+
name="字符串转换错误",
|
665
|
+
attachment_type=allure.attachment_type.TEXT
|
666
|
+
)
|
667
|
+
raise
|
668
|
+
|
669
|
+
|
670
|
+
@keyword_manager.register('转换为整数', [
|
671
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为整数的值'},
|
672
|
+
{'name': '进制', 'mapping': 'base',
|
673
|
+
'description': '数字进制(当值为字符串时)', 'default': 10}
|
674
|
+
])
|
675
|
+
def convert_to_integer(**kwargs):
|
676
|
+
"""将值转换为整数
|
677
|
+
|
678
|
+
Args:
|
679
|
+
value: 要转换的值
|
680
|
+
base: 数字进制(当值为字符串时),默认为10
|
681
|
+
|
682
|
+
Returns:
|
683
|
+
int: 转换后的整数
|
684
|
+
"""
|
685
|
+
value = kwargs.get('value')
|
686
|
+
base = kwargs.get('base', 10)
|
687
|
+
|
688
|
+
if value is None:
|
689
|
+
raise ValueError("值不能为空")
|
690
|
+
|
691
|
+
try:
|
692
|
+
# 如果指定了非10进制,将值转换为字符串再进行进制转换
|
693
|
+
if int(base) != 10:
|
694
|
+
value_str = str(value)
|
695
|
+
result = int(value_str, int(base))
|
696
|
+
else:
|
697
|
+
result = int(value)
|
698
|
+
|
699
|
+
with allure.step(f"转换为整数: {type(value).__name__} -> int"):
|
700
|
+
allure.attach(
|
701
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
702
|
+
f"进制: {base}\n转换结果: {result}\n结果类型: {type(result).__name__}",
|
703
|
+
name="整数转换结果",
|
704
|
+
attachment_type=allure.attachment_type.TEXT
|
705
|
+
)
|
706
|
+
|
707
|
+
return result
|
708
|
+
except Exception as e:
|
709
|
+
allure.attach(
|
710
|
+
f"转换为整数失败: {str(e)}",
|
711
|
+
name="整数转换错误",
|
712
|
+
attachment_type=allure.attachment_type.TEXT
|
713
|
+
)
|
714
|
+
raise
|
715
|
+
|
716
|
+
|
717
|
+
@keyword_manager.register('转换为浮点数', [
|
718
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为浮点数的值'}
|
719
|
+
])
|
720
|
+
def convert_to_float(**kwargs):
|
721
|
+
"""将值转换为浮点数
|
722
|
+
|
723
|
+
Args:
|
724
|
+
value: 要转换的值
|
725
|
+
|
726
|
+
Returns:
|
727
|
+
float: 转换后的浮点数
|
728
|
+
"""
|
729
|
+
value = kwargs.get('value')
|
730
|
+
|
731
|
+
if value is None:
|
732
|
+
raise ValueError("值不能为空")
|
733
|
+
|
734
|
+
try:
|
735
|
+
result = float(value)
|
736
|
+
|
737
|
+
with allure.step(f"转换为浮点数: {type(value).__name__} -> float"):
|
738
|
+
allure.attach(
|
739
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
740
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
741
|
+
name="浮点数转换结果",
|
742
|
+
attachment_type=allure.attachment_type.TEXT
|
743
|
+
)
|
744
|
+
|
745
|
+
return result
|
746
|
+
except Exception as e:
|
747
|
+
allure.attach(
|
748
|
+
f"转换为浮点数失败: {str(e)}",
|
749
|
+
name="浮点数转换错误",
|
750
|
+
attachment_type=allure.attachment_type.TEXT
|
751
|
+
)
|
752
|
+
raise
|
753
|
+
|
754
|
+
|
755
|
+
@keyword_manager.register('转换为布尔值', [
|
756
|
+
{'name': '值', 'mapping': 'value', 'description': '要转换为布尔值的值'}
|
757
|
+
])
|
758
|
+
def convert_to_boolean(**kwargs):
|
759
|
+
"""将值转换为布尔值
|
760
|
+
|
761
|
+
Args:
|
762
|
+
value: 要转换的值
|
763
|
+
|
764
|
+
Returns:
|
765
|
+
bool: 转换后的布尔值
|
766
|
+
"""
|
767
|
+
value = kwargs.get('value')
|
768
|
+
|
769
|
+
try:
|
770
|
+
result = bool(value)
|
771
|
+
|
772
|
+
with allure.step(f"转换为布尔值: {type(value).__name__} -> bool"):
|
773
|
+
allure.attach(
|
774
|
+
f"原始值: {value}\n原始类型: {type(value).__name__}\n"
|
775
|
+
f"转换结果: {result}\n结果类型: {type(result).__name__}",
|
776
|
+
name="布尔值转换结果",
|
777
|
+
attachment_type=allure.attachment_type.TEXT
|
778
|
+
)
|
779
|
+
|
780
|
+
return result
|
781
|
+
except Exception as e:
|
782
|
+
allure.attach(
|
783
|
+
f"转换为布尔值失败: {str(e)}",
|
784
|
+
name="布尔值转换错误",
|
785
|
+
attachment_type=allure.attachment_type.TEXT
|
786
|
+
)
|
787
|
+
raise
|
@@ -7,16 +7,19 @@ from pytest_dsl.core.keyword_manager import keyword_manager, Parameter
|
|
7
7
|
# 配置日志
|
8
8
|
logger = logging.getLogger(__name__)
|
9
9
|
|
10
|
+
|
10
11
|
class RemoteKeywordClient:
|
11
12
|
"""远程关键字客户端,用于连接远程关键字服务器并执行关键字"""
|
12
13
|
|
13
|
-
def __init__(self, url='http://localhost:8270/', api_key=None, alias=None,
|
14
|
+
def __init__(self, url='http://localhost:8270/', api_key=None, alias=None,
|
15
|
+
sync_config=None):
|
14
16
|
self.url = url
|
15
17
|
self.server = xmlrpc.client.ServerProxy(url, allow_none=True)
|
16
18
|
self.keyword_cache = {}
|
17
19
|
self.param_mappings = {} # 存储每个关键字的参数映射
|
18
20
|
self.api_key = api_key
|
19
|
-
self.alias = alias or url.replace('http://', '').replace(
|
21
|
+
self.alias = alias or url.replace('http://', '').replace(
|
22
|
+
'https://', '').split(':')[0]
|
20
23
|
|
21
24
|
# 变量传递配置(简化版)
|
22
25
|
self.sync_config = sync_config or {
|
@@ -24,7 +27,6 @@ class RemoteKeywordClient:
|
|
24
27
|
'sync_yaml_vars': True, # 连接时传递YAML配置变量
|
25
28
|
'yaml_sync_keys': None, # 指定要同步的YAML键列表,None表示同步所有(除了排除的)
|
26
29
|
'yaml_exclude_patterns': [ # 排除包含这些模式的YAML变量
|
27
|
-
'password', 'secret', 'key', 'token', 'credential', 'auth',
|
28
30
|
'private', 'remote_servers' # 排除远程服务器配置避免循环
|
29
31
|
]
|
30
32
|
}
|
@@ -42,7 +44,8 @@ class RemoteKeywordClient:
|
|
42
44
|
self._send_initial_variables()
|
43
45
|
|
44
46
|
logger.info(f"已连接到远程关键字服务器: {self.url}, 别名: {self.alias}")
|
45
|
-
print(f"RemoteKeywordClient: 成功连接到远程服务器 {self.url},
|
47
|
+
print(f"RemoteKeywordClient: 成功连接到远程服务器 {self.url}, "
|
48
|
+
f"别名: {self.alias}")
|
46
49
|
return True
|
47
50
|
except Exception as e:
|
48
51
|
error_msg = f"连接远程关键字服务器失败: {str(e)}"
|
@@ -81,7 +84,8 @@ class RemoteKeywordClient:
|
|
81
84
|
for param_detail in param_details:
|
82
85
|
param_name = param_detail['name']
|
83
86
|
param_mapping_name = param_detail.get('mapping', param_name)
|
84
|
-
param_desc = param_detail.get('description',
|
87
|
+
param_desc = param_detail.get('description',
|
88
|
+
f'远程关键字参数: {param_name}')
|
85
89
|
param_default = param_detail.get('default')
|
86
90
|
|
87
91
|
# 确保参数名称正确映射
|
@@ -114,7 +118,8 @@ class RemoteKeywordClient:
|
|
114
118
|
'func': remote_func,
|
115
119
|
'mapping': {p['name']: p['mapping'] for p in parameters},
|
116
120
|
'parameters': [Parameter(**p) for p in parameters],
|
117
|
-
'defaults': {p['mapping']: p['default'] for p in parameters
|
121
|
+
'defaults': {p['mapping']: p['default'] for p in parameters
|
122
|
+
if p['default'] is not None}, # 添加默认值支持
|
118
123
|
'remote': True, # 标记为远程关键字
|
119
124
|
'alias': self.alias,
|
120
125
|
'original_name': name
|
@@ -161,7 +166,7 @@ class RemoteKeywordClient:
|
|
161
166
|
else:
|
162
167
|
# 如果没有任何映射,使用原始参数名
|
163
168
|
param_mapping = None
|
164
|
-
print(
|
169
|
+
print("没有找到参数映射,使用原始参数名")
|
165
170
|
|
166
171
|
# 映射参数名称
|
167
172
|
mapped_kwargs = {}
|
@@ -209,10 +214,11 @@ class RemoteKeywordClient:
|
|
209
214
|
|
210
215
|
# 处理响应数据
|
211
216
|
if 'response' in return_data and return_data['response']:
|
212
|
-
print(
|
217
|
+
print("远程关键字响应数据: 已接收")
|
213
218
|
|
214
219
|
# 检查是否为新的统一返回格式(包含captures等字段)
|
215
|
-
if 'captures' in return_data or 'session_state' in return_data or
|
220
|
+
if ('captures' in return_data or 'session_state' in return_data or
|
221
|
+
'metadata' in return_data):
|
216
222
|
# 返回完整的新格式,让DSL执行器处理变量捕获
|
217
223
|
return return_data
|
218
224
|
elif 'result' in return_data:
|
@@ -243,11 +249,13 @@ class RemoteKeywordClient:
|
|
243
249
|
if variables_to_send:
|
244
250
|
try:
|
245
251
|
# 调用远程服务器的变量接收接口
|
246
|
-
result = self.server.sync_variables_from_client(
|
252
|
+
result = self.server.sync_variables_from_client(
|
253
|
+
variables_to_send, self.api_key)
|
247
254
|
if result.get('status') == 'success':
|
248
255
|
print(f"成功传递 {len(variables_to_send)} 个变量到远程服务器")
|
249
256
|
else:
|
250
|
-
print(f"传递变量到远程服务器失败:
|
257
|
+
print(f"传递变量到远程服务器失败: "
|
258
|
+
f"{result.get('error', '未知错误')}")
|
251
259
|
except Exception as e:
|
252
260
|
print(f"调用远程变量接口失败: {str(e)}")
|
253
261
|
else:
|
@@ -295,10 +303,12 @@ class RemoteKeywordClient:
|
|
295
303
|
# 获取所有YAML变量
|
296
304
|
yaml_data = yaml_vars._variables
|
297
305
|
if yaml_data:
|
306
|
+
print(f"客户端YAML变量总数: {len(yaml_data)}")
|
307
|
+
|
298
308
|
# 检查同步配置中是否指定了特定的键
|
299
309
|
sync_keys = self.sync_config.get('yaml_sync_keys', None)
|
300
310
|
exclude_patterns = self.sync_config.get('yaml_exclude_patterns', [
|
301
|
-
'password', 'secret', '
|
311
|
+
'password', 'secret', 'token', 'credential', 'auth',
|
302
312
|
'private', 'remote_servers' # 排除远程服务器配置避免循环
|
303
313
|
])
|
304
314
|
|
@@ -307,6 +317,7 @@ class RemoteKeywordClient:
|
|
307
317
|
for key in sync_keys:
|
308
318
|
if key in yaml_data:
|
309
319
|
variables[key] = yaml_data[key]
|
320
|
+
print(f"传递指定YAML变量: {key}")
|
310
321
|
else:
|
311
322
|
# 传递所有YAML变量,但排除敏感信息
|
312
323
|
for key, value in yaml_data.items():
|
@@ -323,7 +334,8 @@ class RemoteKeywordClient:
|
|
323
334
|
if not should_exclude and isinstance(value, str):
|
324
335
|
value_lower = value.lower()
|
325
336
|
for pattern in exclude_patterns:
|
326
|
-
if pattern.lower() in value_lower and
|
337
|
+
if (pattern.lower() in value_lower and
|
338
|
+
len(value) < 100): # 只检查短字符串
|
327
339
|
should_exclude = True
|
328
340
|
break
|
329
341
|
|
@@ -336,11 +348,11 @@ class RemoteKeywordClient:
|
|
336
348
|
|
337
349
|
except Exception as e:
|
338
350
|
logger.warning(f"收集YAML变量失败: {str(e)}")
|
351
|
+
print(f"收集YAML变量失败: {str(e)}")
|
339
352
|
|
340
353
|
return variables
|
341
354
|
|
342
355
|
|
343
|
-
|
344
356
|
# 远程关键字客户端管理器
|
345
357
|
class RemoteKeywordManager:
|
346
358
|
"""远程关键字客户端管理器,管理多个远程服务器连接"""
|
@@ -348,7 +360,8 @@ class RemoteKeywordManager:
|
|
348
360
|
def __init__(self):
|
349
361
|
self.clients = {} # 别名 -> 客户端实例
|
350
362
|
|
351
|
-
def register_remote_server(self, url, alias, api_key=None,
|
363
|
+
def register_remote_server(self, url, alias, api_key=None,
|
364
|
+
sync_config=None):
|
352
365
|
"""注册远程关键字服务器
|
353
366
|
|
354
367
|
Args:
|
@@ -361,7 +374,8 @@ class RemoteKeywordManager:
|
|
361
374
|
bool: 是否成功连接
|
362
375
|
"""
|
363
376
|
print(f"RemoteKeywordManager: 正在注册远程服务器 {url} 别名 {alias}")
|
364
|
-
client = RemoteKeywordClient(url=url, api_key=api_key, alias=alias,
|
377
|
+
client = RemoteKeywordClient(url=url, api_key=api_key, alias=alias,
|
378
|
+
sync_config=sync_config)
|
365
379
|
success = client.connect()
|
366
380
|
|
367
381
|
if success:
|
@@ -394,6 +408,5 @@ class RemoteKeywordManager:
|
|
394
408
|
return client._execute_remote_keyword(name=keyword_name, **kwargs)
|
395
409
|
|
396
410
|
|
397
|
-
|
398
411
|
# 创建全局远程关键字管理器实例
|
399
412
|
remote_keyword_manager = RemoteKeywordManager()
|
@@ -403,9 +403,26 @@ class RemoteKeywordServer:
|
|
403
403
|
self.shared_variables[name] = value
|
404
404
|
print(f"接收到客户端变量: {name}")
|
405
405
|
|
406
|
+
# 将所有同步的变量直接注入到yaml_vars中,实现无缝访问
|
407
|
+
from pytest_dsl.core.yaml_vars import yaml_vars
|
408
|
+
|
409
|
+
for name, value in variables.items():
|
410
|
+
# 直接设置到yaml_vars中,确保所有关键字都能无缝访问
|
411
|
+
yaml_vars._variables[name] = value
|
412
|
+
print(f"✓ 变量 {name} 已注入到yaml_vars,实现无缝访问")
|
413
|
+
|
414
|
+
# 同时处理全局变量到global_context
|
415
|
+
from pytest_dsl.core.global_context import global_context
|
416
|
+
for name, value in variables.items():
|
417
|
+
if name.startswith('g_'):
|
418
|
+
global_context.set_variable(name, value)
|
419
|
+
print(f"✓ 全局变量 {name} 已注入到global_context")
|
420
|
+
|
421
|
+
print(f"✅ 总共同步了 {len(variables)} 个变量,全部实现无缝访问")
|
422
|
+
|
406
423
|
return {
|
407
424
|
'status': 'success',
|
408
|
-
'message': f'成功同步 {len(variables)}
|
425
|
+
'message': f'成功同步 {len(variables)} 个变量,全部实现无缝访问'
|
409
426
|
}
|
410
427
|
except Exception as e:
|
411
428
|
return {
|