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 | __author__ = "Dennis Stam" |
---|
17 | __version__ = ( 1, 1, 0 ) |
---|
18 | |
---|
19 | class AdvancedParser(OptionParser): |
---|
20 | """ |
---|
21 | This class extends from OptionParser, where the method check_values has been |
---|
22 | overrided. |
---|
23 | |
---|
24 | In this function a extra parsing is done on the the rest of the arguments. This |
---|
25 | extra parsing is the creating of multiple hostnames from a pattern/range. |
---|
26 | |
---|
27 | When a user specifies this argument dr-r[15,17-20]n[1-5,10] then this class |
---|
28 | returns 24 hosts. Besides using numbers you can also specify lower cased |
---|
29 | letters from a-z. |
---|
30 | |
---|
31 | Doctest: |
---|
32 | >>> parser = AdvancedParser() |
---|
33 | >>> parser.return_range('12-15,20') |
---|
34 | [12, 13, 14, 15, '20'] |
---|
35 | |
---|
36 | >>> option, args = parser.parse_args(['dr-r7n[1-5]']) |
---|
37 | >>> print args |
---|
38 | ['dr-r7n1', 'dr-r7n2', 'dr-r7n3', 'dr-r7n4', 'dr-r7n5'] |
---|
39 | """ |
---|
40 | |
---|
41 | def parse_letters( self, chars ): |
---|
42 | |
---|
43 | if type( chars ) is not types.ListType and len( chars ) != 2: |
---|
44 | self.error( 'Could not parse range' ) |
---|
45 | |
---|
46 | start = ord( chars[ 0 ] ) |
---|
47 | end = ord( chars[ 1 ] ) |
---|
48 | out = list() |
---|
49 | |
---|
50 | if start > 96 and start < 122 and end > 97 and end < 123: |
---|
51 | lrange = range( start, end + 1 ) |
---|
52 | |
---|
53 | for ichar in lrange: |
---|
54 | out.append( chr( ichar ) ) |
---|
55 | |
---|
56 | return out |
---|
57 | else: |
---|
58 | self.error( 'AdvanedParser module only handles letters from a to z ( lower cased )' ) |
---|
59 | |
---|
60 | def return_range(self, string): |
---|
61 | """ |
---|
62 | This method uses the given numbers and converts them to ranges. When |
---|
63 | ower cased letters are specified they will be converted to integer |
---|
64 | ordinal of a one-character string. |
---|
65 | (ie. a = 97, z = 122) |
---|
66 | |
---|
67 | The ranges will be return as lists |
---|
68 | """ |
---|
69 | numbers_chars = list() |
---|
70 | equal_width_length = 0 |
---|
71 | |
---|
72 | if string.find( ',' ) >= 0 and string.find( ',' ) < 1: |
---|
73 | self.error( 'You cannot start a range with \',\'. Given range: %s' % string ) |
---|
74 | |
---|
75 | if string.find( '-' ) >= 0 and string.find( '-' ) < 1: |
---|
76 | self.error( 'You cannot start a range with \'-\'. Given range: %s' % string ) |
---|
77 | |
---|
78 | for section in string.split( ',' ): |
---|
79 | if section.find( '-' ) > 0: |
---|
80 | if len( section.split( '-' ) ) != 2: |
---|
81 | self.error( 'A range must be consisted of two values' ) |
---|
82 | |
---|
83 | chars = section.split( '-' ) |
---|
84 | |
---|
85 | try: |
---|
86 | if chars[ 0 ][ 0 ] == '0' or chars[ 1 ][ 0 ]: |
---|
87 | if len( chars[ 0 ] ) > len( chars[ 1 ] ): |
---|
88 | equal_width_length = len( chars[ 0 ] ) |
---|
89 | else: |
---|
90 | equal_width_lenght = len( chars[ 1 ] ) |
---|
91 | |
---|
92 | numbers_chars += range( int( chars[ 0 ] ), int( chars[ 1 ] ) + 1 ) |
---|
93 | except ValueError: |
---|
94 | numbers_chars += self.parse_letters( chars ) |
---|
95 | |
---|
96 | else: |
---|
97 | if equal_width_length != 0 and len( section ) > equal_width_lenght: |
---|
98 | equal_width_lenght = len( section ) |
---|
99 | numbers_chars.append( section ) |
---|
100 | |
---|
101 | return numbers_chars |
---|
102 | |
---|
103 | def combine( self, pre, post): |
---|
104 | ''' |
---|
105 | This method is used to combine a possibility of a combination |
---|
106 | ''' |
---|
107 | if pre == '': |
---|
108 | return post |
---|
109 | else: |
---|
110 | return '%s %s' % (pre, post) |
---|
111 | |
---|
112 | def combinations( self, listin, prefix=''): |
---|
113 | ''' |
---|
114 | This method creates from the given ranges all possible combinations |
---|
115 | ''' |
---|
116 | outlist = list() |
---|
117 | |
---|
118 | if len( listin ) > 1: |
---|
119 | for item in listin[0]: |
---|
120 | outlist += self.combinations( listin[1:], self.combine( prefix, str( item ) ) ) |
---|
121 | else: |
---|
122 | for item in listin[0]: |
---|
123 | outlist.append( tuple( self.combine( prefix, str( item ) ).split( ' ') ) ) |
---|
124 | |
---|
125 | return outlist |
---|
126 | |
---|
127 | def args_parser(self, args): |
---|
128 | ''' |
---|
129 | This method checks all given extra arguments for the given ranges between the |
---|
130 | [ and ] |
---|
131 | ''' |
---|
132 | findregex = re.compile( r'\[([0-9a-z\-,]+)\]', re.VERBOSE ) |
---|
133 | nodenames = list() |
---|
134 | |
---|
135 | for arg in args: |
---|
136 | found = findregex.findall( arg ) |
---|
137 | ranges = list() |
---|
138 | |
---|
139 | if found: |
---|
140 | pattern = findregex.sub( '%s', arg ) |
---|
141 | |
---|
142 | for part in found: |
---|
143 | ranges.append( self.return_range( part ) ) |
---|
144 | |
---|
145 | combs = self.combinations( ranges ) |
---|
146 | |
---|
147 | for comb in combs: |
---|
148 | # Here the %s in the pattern are |
---|
149 | # replaced by the correct value |
---|
150 | nodenames.append( pattern % comb ) |
---|
151 | else: |
---|
152 | nodenames.append( arg ) |
---|
153 | |
---|
154 | return nodenames |
---|
155 | |
---|
156 | def check_values(self, values, args): |
---|
157 | ''' |
---|
158 | Here we override the default method in OptionParser to |
---|
159 | enable our extra parsing on the given Arguments |
---|
160 | ''' |
---|
161 | return values, self.args_parser( args ) |
---|
162 | |
---|
163 | if __name__ == "__main__": |
---|
164 | import doctest |
---|
165 | doctest.testmod() |
---|