@@ -752,6 +752,14 @@ def __ixor__(self, other):
752752 return self
753753
754754
755+ def _strip_escape (nsstr ):
756+ """
757+ Helper to prepare a nodeset string for parsing: trim boundary
758+ whitespaces and escape special characters.
759+ """
760+ return nsstr .strip ().replace ('%' , '%%' )
761+
762+
755763class ParsingEngine (object ):
756764 """
757765 Class that is able to transform a source into a NodeSetBase.
@@ -801,6 +809,7 @@ def parse_string(self, nsstr, autostep, namespace=None):
801809 Return a NodeSetBase object.
802810 """
803811 nodeset = NodeSetBase ()
812+ nsstr = _strip_escape (nsstr )
804813
805814 for opc , pat , rgnd in self ._scan_string (nsstr , autostep ):
806815 # Parser main debugging:
@@ -825,8 +834,8 @@ def parse_string(self, nsstr, autostep, namespace=None):
825834
826835 def parse_string_single (self , nsstr , autostep ):
827836 """Parse provided string and return a NodeSetBase object."""
828- # ignore node boundary whitespace(s)
829- pat , rangesets = self . _scan_string_single ( nsstr . strip (), autostep )
837+ pat , rangesets = self . _scan_string_single ( _strip_escape ( nsstr ),
838+ autostep )
830839 if len (rangesets ) > 1 :
831840 rgobj = RangeSetND ([rangesets ], None , autostep , copy_rangeset = False )
832841 elif len (rangesets ) == 1 :
@@ -861,11 +870,14 @@ def parse_group_string(self, nodegroup, namespace=None):
861870 return ',' .join (reslist ), namespace
862871
863872 def grouplist (self , namespace = None ):
864- """Return a sorted list of groups from current resolver (in optional
865- group source / namespace)."""
873+ """
874+ Return a sorted list of groups from current resolver (in optional
875+ group source / namespace).
876+ """
866877 grpset = NodeSetBase ()
867878 for grpstr in self .group_resolver .grouplist (namespace ):
868879 # We scan each group string to expand any range seen...
880+ grpstr = _strip_escape (grpstr )
869881 for opc , pat , rgnd in self ._scan_string (grpstr , None ):
870882 getattr (grpset , opc )(NodeSetBase (pat , rgnd , False ))
871883 return list (grpset )
@@ -947,20 +959,16 @@ def _scan_string_single(self, nsstr, autostep):
947959
948960 def _scan_string (self , nsstr , autostep ):
949961 """Parsing engine's string scanner method (iterator)."""
950- pat = nsstr .strip ()
951- # avoid misformatting
952- if pat .find ('%' ) >= 0 :
953- pat = pat .replace ('%' , '%%' )
954962 next_op_code = 'update'
955- while pat is not None :
963+ while nsstr is not None :
956964 # Ignore whitespace(s) for convenience
957- pat = pat .lstrip ()
965+ nsstr = nsstr .lstrip ()
958966
959967 rsets = []
960968 op_code = next_op_code
961969
962- op_idx , next_op_code = self ._next_op (pat )
963- bracket_idx = pat .find (self .BRACKET_OPEN )
970+ op_idx , next_op_code = self ._next_op (nsstr )
971+ bracket_idx = nsstr .find (self .BRACKET_OPEN )
964972
965973 # Check if the operator is after the bracket, or if there
966974 # is no operator at all but some brackets.
@@ -970,13 +978,13 @@ def _scan_string(self, nsstr, autostep):
970978 # Fill prefix, range and suffix from pattern
971979 # eg. "forbin[3,4-10]-ilo" -> "forbin", "3,4-10", "-ilo"
972980 newpat = ""
973- sfx = pat
981+ sfx = nsstr
974982 while bracket_idx >= 0 and (op_idx > bracket_idx or op_idx < 0 ):
975983 pfx , sfx = sfx .split (self .BRACKET_OPEN , 1 )
976984 try :
977985 rng , sfx = sfx .split (self .BRACKET_CLOSE , 1 )
978986 except ValueError :
979- raise NodeSetParseError (pat , "missing bracket" )
987+ raise NodeSetParseError (nsstr , "missing bracket" )
980988
981989 # illegal closing bracket checks
982990 if pfx .find (self .BRACKET_CLOSE ) > - 1 :
@@ -995,7 +1003,7 @@ def _scan_string(self, nsstr, autostep):
9951003
9961004 # pfx + sfx cannot be empty
9971005 if pfxlen + sfxlen == 0 :
998- raise NodeSetParseError (pat , "empty node name" )
1006+ raise NodeSetParseError (nsstr , "empty node name" )
9991007
10001008 if sfxlen > 0 :
10011009 # amending trailing digits generates /steps
@@ -1031,12 +1039,12 @@ def _scan_string(self, nsstr, autostep):
10311039 # Check if we have a next op-separated node or pattern
10321040 op_idx , next_op_code = self ._next_op (sfx )
10331041 if op_idx < 0 :
1034- pat = None
1042+ nsstr = None
10351043 else :
10361044 opc = self .OP_CODES [next_op_code ]
1037- sfx , pat = sfx .split (opc , 1 )
1045+ sfx , nsstr = sfx .split (opc , 1 )
10381046 # Detected character operator so right operand is mandatory
1039- if not pat :
1047+ if not nsstr :
10401048 msg = "missing nodeset operand with '%s' operator" % opc
10411049 raise NodeSetParseError (None , msg )
10421050
@@ -1049,22 +1057,22 @@ def _scan_string(self, nsstr, autostep):
10491057
10501058 # pfx + sfx cannot be empty
10511059 if len (newpat ) == 0 :
1052- raise NodeSetParseError (pat , "empty node name" )
1060+ raise NodeSetParseError (nsstr , "empty node name" )
10531061
10541062 else :
10551063 # In this case, either there is no comma and no bracket,
10561064 # or the bracket is after the comma, then just return
10571065 # the node.
10581066 if op_idx < 0 :
1059- node = pat
1060- pat = None # break next time
1067+ node = nsstr
1068+ nsstr = None # break next time
10611069 else :
10621070 opc = self .OP_CODES [next_op_code ]
1063- node , pat = pat .split (opc , 1 )
1071+ node , nsstr = nsstr .split (opc , 1 )
10641072 # Detected character operator so both operands are mandatory
1065- if not node or not pat :
1073+ if not node or not nsstr :
10661074 msg = "missing nodeset operand with '%s' operator" % opc
1067- raise NodeSetParseError (node or pat , msg )
1075+ raise NodeSetParseError (node or nsstr , msg )
10681076
10691077 # Check for illegal closing bracket
10701078 if node .find (self .BRACKET_CLOSE ) > - 1 :
0 commit comments