@@ -89,11 +89,11 @@ def get_xml_nodes(xml_root, name):
89
89
>>> ################ INVALID SYNTAX #######################
90
90
>>> get_xml_nodes(tree,'sub::::prop1')
91
91
Traceback (most recent call last):
92
- SystemExit: ERROR: Invalid xml node name format, 'sub::::prop1' contains ::::
92
+ SystemExit: ERROR: Invalid xml node name format, 'sub::::prop1' contains ' ::::'
93
93
>>> ################ VALID USAGE #######################
94
94
>>> get_xml_nodes(tree,'invalid::prop1')
95
95
[]
96
- >>> [item.text for item in get_xml_nodes(tree,'prop1')]
96
+ >>> [item.text for item in get_xml_nodes(tree,'ANY:: prop1')]
97
97
['one', 'two']
98
98
>>> [item.text for item in get_xml_nodes(tree,'::prop1')]
99
99
['one']
@@ -102,10 +102,10 @@ def get_xml_nodes(xml_root, name):
102
102
>>> item = get_xml_nodes(tree,'prop2')[0]
103
103
>>> parent_map = create_parent_map(tree)
104
104
>>> [p.tag for p in get_parents(item, parent_map)]
105
- ['root', ' sub']
105
+ ['sub']
106
106
"""
107
107
expect ('::::' not in name ,
108
- "Badly formatted node name: found '::::'" )
108
+ f"Invalid xml node name format, ' { name } ' contains '::::'" )
109
109
110
110
tokens = name .split ("::" )
111
111
expect (tokens [- 1 ] != '' , "Input query string ends with '::'. It should end with an actual node name" )
@@ -156,7 +156,8 @@ def get_xml_nodes(xml_root, name):
156
156
error_str = f"{ name } is ambiguous. Use ANY in the node path to allow multiple matches. Matches:\n "
157
157
for node in result :
158
158
parents = get_parents (node , parent_map )
159
- name = "::" .join (e .tag for e in parents ) + "::" + node .tag
159
+ name = "::" .join (e .tag for e in parents )
160
+ name = node .tag if name == "" else name + "::" + node .tag
160
161
error_str += " " + name + "\n "
161
162
162
163
expect (False , error_str )
@@ -393,8 +394,8 @@ def atm_config_chg_impl(xml_root, change):
393
394
>>> atm_config_chg_impl(tree,'prop1=three')
394
395
Traceback (most recent call last):
395
396
SystemExit: ERROR: prop1 is ambiguous. Use ANY in the node path to allow multiple matches. Matches:
396
- root:: prop1
397
- root:: sub::prop1
397
+ prop1
398
+ sub::prop1
398
399
<BLANKLINE>
399
400
>>> ################ VALID USAGE #######################
400
401
>>> atm_config_chg_impl(tree,'::prop1=two')
@@ -405,7 +406,7 @@ def atm_config_chg_impl(xml_root, change):
405
406
True
406
407
>>> atm_config_chg_impl(tree,'ANY::prop1=three')
407
408
True
408
- >>> [item.text for item in get_xml_nodes(tree,'prop1')]
409
+ >>> [item.text for item in get_xml_nodes(tree,'ANY:: prop1')]
409
410
['three', 'three']
410
411
>>> ################ TEST APPEND += #################
411
412
>>> atm_config_chg_impl(tree,'a+=4')
@@ -465,14 +466,49 @@ def get_parents(elem, parent_map):
465
466
be the furthest ancestor, last item will be direct parent)
466
467
"""
467
468
results = []
468
- if elem in parent_map and parent_map [ elem ] is not None :
469
+ if not is_root ( elem , parent_map ) :
469
470
parent = parent_map [elem ]
470
471
results = get_parents (parent , parent_map )
471
- if parent_map [ parent ] is not None :
472
+ if not is_root ( parent , parent_map ) :
472
473
results .append (parent )
473
474
474
475
return results
475
476
477
+ ###############################################################################
478
+ def is_anchestor_of (parent ,child ,parent_map ):
479
+ ###############################################################################
480
+ """
481
+ >>> xml = '''
482
+ ... <root>
483
+ ... <prop1>one</prop1>
484
+ ... <sub>
485
+ ... <prop1>two</prop1>
486
+ ... <prop2 type="integer" valid_values="1,2">2</prop2>
487
+ ... </sub>
488
+ ... </root>
489
+ ... '''
490
+ >>> import xml.etree.ElementTree as ET
491
+ >>> tree = ET.fromstring(xml)
492
+ >>> parent_map = create_parent_map(tree)
493
+ >>> sub = get_xml_nodes(tree,'sub')[0]
494
+ >>> sub_prop1 = get_xml_nodes(tree,'sub::prop1')[0]
495
+ >>> is_anchestor_of(sub,sub_prop1,parent_map)
496
+ True
497
+ >>> is_anchestor_of(sub_prop1,sub,parent_map)
498
+ False
499
+ """
500
+ curr = child
501
+ while curr is not None :
502
+ if curr is parent :
503
+ return True
504
+ curr = parent_map [curr ]
505
+ return False
506
+
507
+ ###############################################################################
508
+ def is_root (node ,parent_map ):
509
+ ###############################################################################
510
+ return parent_map [node ] is None
511
+
476
512
###############################################################################
477
513
def print_var_impl (node ,parent_map ,full ,dtype ,value ,valid_values ,print_style = "invalid" ,indent = "" ):
478
514
###############################################################################
@@ -482,21 +518,23 @@ def print_var_impl(node,parent_map,full,dtype,value,valid_values,print_style="in
482
518
# This is not a leaf, so print all nested nodes.
483
519
for child in node :
484
520
# Since prints are nicely nested, use 'node-name' as print style
485
- print_var_impl (child ,{},full ,dtype ,value ,valid_values ,'node-name' ,indent + " " )
521
+ print_var_impl (child ,{},full ,dtype ,value ,valid_values ,'node-name' ,indent + " " )
486
522
return
487
523
488
- # print (f"printing leaf={node.tag}, len(parent_map)={len(parent_map)}")
489
524
expect (print_style in ["node-name" ,"full-scope" ,"parent-scope" ],
490
525
f"Invalid print_style '{ print_style } ' for print_var_impl. Use 'full' or 'short'." )
491
526
492
527
if print_style == "node-name" :
493
528
# Just the inner most name
494
529
name = node .tag
495
530
elif print_style == "parent-scope" :
496
- name = parent_map [node ].tag + "::" + node .tag
531
+ parent = parent_map [node ]
532
+ name = node .tag if is_root (parent ,parent_map ) else parent .tag + "::" + node .tag
497
533
else :
498
534
parents = get_parents (node , parent_map )
499
- name = "::" .join (e .tag for e in parents ) + "::" + node .tag
535
+ name = "::" .join (e .tag for e in parents )
536
+ name += "::" if parents else ""
537
+ name += node .tag
500
538
501
539
if full :
502
540
expect ("type" in node .attrib .keys (),
@@ -563,7 +601,19 @@ def print_var(xml_root,parent_map,var,full,dtype,value,valid_values,print_style=
563
601
# Get matches
564
602
matches = get_xml_nodes (xml_root ,var )
565
603
566
- for node in matches :
604
+ # If ANY is in the var name, we may hit the case where one of the matches
605
+ # is a parent of another match. In this case, we want to get rid of
606
+ unique_matches = []
607
+ for i in matches :
608
+ add_this = True
609
+ for j in matches :
610
+ if i is not j and is_anchestor_of (j ,i ,parent_map ):
611
+ add_this = False
612
+
613
+ if add_this :
614
+ unique_matches .append (i )
615
+
616
+ for node in unique_matches :
567
617
print_var_impl (node ,parent_map ,full ,dtype ,value ,valid_values ,print_style ,indent )
568
618
569
619
###############################################################################
@@ -584,23 +634,22 @@ def atm_query_impl(xml_root,variables,listall=False,full=False,value=False,
584
634
>>> tree = ET.fromstring(xml)
585
635
>>> vars = ['prop2','::prop1']
586
636
>>> success = atm_query_impl(tree, vars)
587
- root:: sub::prop2: 2
588
- root:: prop1: one
637
+ sub::prop2: 2
638
+ prop1: one
589
639
>>> success = atm_query_impl(tree, [], listall=True, valid_values=True)
590
- root
640
+ prop1: <valid values not provided>
641
+ sub:
591
642
prop1: <valid values not provided>
592
- sub
593
- prop1: <valid values not provided>
594
- prop2: ['1', '2']
643
+ prop2: ['1', '2']
595
644
>>> success = atm_query_impl(tree,['prop1'], grep=True)
596
- root:: prop1: one
645
+ prop1: one
597
646
sub::prop1: two
598
647
"""
599
648
600
649
if not parent_map :
601
650
parent_map = create_parent_map (xml_root )
602
651
if listall :
603
- print_var (xml_root ,parent_map ,'ANY' ,full ,dtype ,value ,valid_values ,"node-name" ," " )
652
+ print_var (xml_root ,parent_map ,'ANY' ,full ,dtype ,value ,valid_values ,"node-name" ," " )
604
653
605
654
elif grep :
606
655
for regex in variables :
@@ -610,7 +659,7 @@ def atm_query_impl(xml_root,variables,listall=False,full=False,value=False,
610
659
if len (xml_root )> 0 :
611
660
parents = get_parents (xml_root ,parent_map )
612
661
print (f"{ '::' .join ([p .tag for p in parents ]) + '::' + xml_root .tag } :" )
613
- print_var (xml_root ,parent_map ,'ANY' ,full ,dtype ,value ,valid_values ,"node-name" ," " )
662
+ print_var (xml_root ,parent_map ,'ANY' ,full ,dtype ,value ,valid_values ,"node-name" ," " )
614
663
else :
615
664
for elem in xml_root :
616
665
if len (elem )> 0 :
@@ -624,6 +673,6 @@ def atm_query_impl(xml_root,variables,listall=False,full=False,value=False,
624
673
else :
625
674
for var in variables :
626
675
pmap = {} if var == 'ANY' else parent_map
627
- print_var (xml_root ,pmap ,var ,full ,dtype ,value ,valid_values ,"parent-scope" ," " )
676
+ print_var (xml_root ,pmap ,var ,full ,dtype ,value ,valid_values ,"parent-scope" ," " )
628
677
629
678
return True
0 commit comments