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() |
---|