[142] | 1 | # |
---|
| 2 | # Author: Dennis Stam |
---|
| 3 | # Date : 09-04-2009 |
---|
| 4 | # Desc. : This class allows you use ranges within the given arguments. Very |
---|
| 5 | # useful for specifying mutliple hosts. This class extends the |
---|
| 6 | # OptionParser class. |
---|
| 7 | # |
---|
| 8 | # SVN Info: |
---|
| 9 | # $Id: AdvancedParser.py 3667 2009-04-15 12:02:04Z dennis $ |
---|
| 10 | # $URL: https://subtrac.sara.nl/hpcv/svn/beowulf/trunk/sara_python_modules/AdvancedParser.py $ |
---|
| 11 | # |
---|
| 12 | from optparse import OptionParser |
---|
| 13 | import re |
---|
| 14 | import types |
---|
| 15 | |
---|
| 16 | class AdvancedParser(OptionParser): |
---|
| 17 | """ |
---|
| 18 | This class extends from OptionParser, where the method check_values has been |
---|
| 19 | overrided. |
---|
| 20 | |
---|
| 21 | In this function a extra parsing is done on the the rest of the arguments. This |
---|
| 22 | extra parsing is the creating of multiple hostnames from a pattern/range. |
---|
| 23 | |
---|
| 24 | When a user specifies this argument dr-r[15,17-20]n[1-5,10] then this class |
---|
| 25 | returns 24 hosts. Besides using numbers you can also specify lower cased |
---|
| 26 | letters from a-z. |
---|
| 27 | |
---|
| 28 | Doctest: |
---|
| 29 | >>> parser = AdvancedParser() |
---|
| 30 | >>> parser.return_range('12-15,20') |
---|
| 31 | [12, 13, 14, 15, '20'] |
---|
| 32 | |
---|
| 33 | >>> option, args = parser.parse_args(['dr-r7n[1-5]']) |
---|
| 34 | >>> print args |
---|
| 35 | ['dr-r7n1', 'dr-r7n2', 'dr-r7n3', 'dr-r7n4', 'dr-r7n5'] |
---|
| 36 | """ |
---|
| 37 | |
---|
| 38 | def return_range(self, string): |
---|
| 39 | """ |
---|
| 40 | This method uses the given numbers and converts them to ranges. When |
---|
| 41 | ower cased letters are specified they will be converted to integer |
---|
| 42 | ordinal of a one-character string. |
---|
| 43 | (ie. a = 97, z = 122) |
---|
| 44 | |
---|
| 45 | The ranges will be return as lists |
---|
| 46 | """ |
---|
| 47 | parts = string.split( ',' ) |
---|
| 48 | numbers_chars = list() |
---|
| 49 | equal_width_length = 0 |
---|
| 50 | |
---|
| 51 | for part in parts: |
---|
| 52 | part_range = part.split( '-' ) |
---|
| 53 | if len( part_range ) == 2: |
---|
| 54 | try: |
---|
| 55 | if part_range[0][0] == '0' or part_range[1][0] == '0': |
---|
| 56 | if len( part_range[0] ) > len( part_range[1] ): |
---|
| 57 | equal_width_length = len( part_range[0] ) |
---|
| 58 | else: |
---|
| 59 | equal_width_length = len( part_range[1] ) |
---|
| 60 | |
---|
| 61 | numbers_chars += range( int( part_range[0] ), int( part_range[1] ) + 1 ) |
---|
| 62 | except ValueError: |
---|
| 63 | begin = ord( part_range[0] ) |
---|
| 64 | end = ord( part_range[1] ) |
---|
| 65 | tmplist = list() |
---|
| 66 | |
---|
| 67 | if begin > 96 and end < 123: |
---|
| 68 | tmplist = range( begin, end + 1) |
---|
| 69 | |
---|
| 70 | for letter in tmplist: |
---|
| 71 | numbers_chars.append( chr( letter ) ) |
---|
| 72 | else: |
---|
| 73 | if equal_width_length != 0 and len( part ) > equal_width_length: |
---|
| 74 | equal_width_length = len( part ) |
---|
| 75 | |
---|
| 76 | numbers_chars.append( part ) |
---|
| 77 | |
---|
| 78 | if equal_width_length > 0: |
---|
| 79 | tmplist = list() |
---|
| 80 | |
---|
| 81 | for number_char in numbers_chars: |
---|
| 82 | try: |
---|
| 83 | nnum = int( number_char ) |
---|
| 84 | tmplist.append( '%0*d' % (equal_width_length, nnum) ) |
---|
| 85 | except ValueError: |
---|
| 86 | tmplist.append( number_char ) |
---|
| 87 | |
---|
| 88 | numbers_chars = tmplist |
---|
| 89 | |
---|
| 90 | return numbers_chars |
---|
| 91 | |
---|
| 92 | def combine( self, pre, post): |
---|
| 93 | ''' |
---|
| 94 | This method is used to combine a possibility of a combination |
---|
| 95 | ''' |
---|
| 96 | if pre == '': |
---|
| 97 | return post |
---|
| 98 | else: |
---|
| 99 | return '%s %s' % (pre, post) |
---|
| 100 | |
---|
| 101 | def combinations( self, listin, prefix=''): |
---|
| 102 | ''' |
---|
| 103 | This method creates from the given ranges all possible combinations |
---|
| 104 | ''' |
---|
| 105 | outlist = list() |
---|
| 106 | |
---|
| 107 | if len( listin ) > 1: |
---|
| 108 | for item in listin[0]: |
---|
| 109 | outlist += self.combinations( listin[1:], self.combine( prefix, str( item ) ) ) |
---|
| 110 | else: |
---|
| 111 | for item in listin[0]: |
---|
| 112 | outlist.append( tuple( self.combine( prefix, str( item ) ).split( ' ') ) ) |
---|
| 113 | |
---|
| 114 | return outlist |
---|
| 115 | |
---|
| 116 | def args_parser(self, args): |
---|
| 117 | ''' |
---|
| 118 | This method checks all given extra arguments for the given ranges between the |
---|
| 119 | [ and ] |
---|
| 120 | ''' |
---|
| 121 | findregex = re.compile( r'\[([0-9a-z\-,]+)\]', re.VERBOSE ) |
---|
| 122 | nodenames = list() |
---|
| 123 | |
---|
| 124 | for arg in args: |
---|
| 125 | found = findregex.findall( arg ) |
---|
| 126 | ranges = list() |
---|
| 127 | |
---|
| 128 | if found: |
---|
| 129 | pattern = findregex.sub( '%s', arg ) |
---|
| 130 | |
---|
| 131 | for part in found: |
---|
| 132 | ranges.append( self.return_range( part ) ) |
---|
| 133 | |
---|
| 134 | combs = self.combinations( ranges ) |
---|
| 135 | |
---|
| 136 | for comb in combs: |
---|
| 137 | # Here the %s in the pattern are |
---|
| 138 | # replaced by the correct value |
---|
| 139 | nodenames.append( pattern % comb ) |
---|
| 140 | else: |
---|
| 141 | nodenames.append( arg ) |
---|
| 142 | |
---|
| 143 | return nodenames |
---|
| 144 | |
---|
| 145 | def check_values(self, values, args): |
---|
| 146 | ''' |
---|
| 147 | Here we override the default method in OptionParser to |
---|
| 148 | enable our extra parsing on the given Arguments |
---|
| 149 | ''' |
---|
| 150 | return values, self.args_parser( args ) |
---|
| 151 | |
---|
| 152 | if __name__ == "__main__": |
---|
| 153 | import doctest |
---|
| 154 | print 'Starting doctest' |
---|
| 155 | doctest.testmod() |
---|