source: trunk/src/AdvancedParser.py @ 269

Last change on this file since 269 was 269, checked in by dennis, 13 years ago

Fixed AdvancedParser? when using range 01-12

File size: 6.7 KB
Line 
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 5338 2011-01-19 13:09:09Z dennis $
10#       $URL: https://subtrac.sara.nl/osd/beowulf/svn/trunk/python_modules/advancedparser/AdvancedParser.py $
11#
12from optparse import OptionParser
13import re
14import types
15
16__author__  = "Dennis Stam"
17__version__ = ( 1, 1, 2 )
18
19class 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                    tmp_list = list()
102                    if equal_width_length > 0:
103                        for number_char in numbers_chars:
104                            try:
105                                nnum = int( number_char )
106                                tmp_list.append( '%0*d' % ( equal_width_length, nnum ) )
107                            except ValueError:
108                                tmp_list.append( number_char )
109                        numbers_chars = tmp_list
110
111                return numbers_chars
112
113        def combine( self, pre, post):
114                '''
115                This method is used to combine a possibility of a combination
116                '''
117                if pre == '':
118                        return post
119                else:
120                        return '%s %s' % (pre, post)
121
122        def combinations( self, listin, prefix=''):
123                '''
124                This method creates from the given ranges all possible combinations
125                '''
126                outlist = list()
127
128                if len( listin ) > 1:
129                        for item in listin[0]:
130                                outlist += self.combinations( listin[1:], self.combine( prefix, str( item ) ) )
131                else:
132                        for item in listin[0]:
133                                outlist.append( tuple( self.combine( prefix, str( item ) ).split( ' ') ) )
134
135                return outlist
136
137        def args_parser(self, args):
138                '''
139                This method checks all given extra arguments for the given ranges between the
140                [ and ]
141                '''
142                findregex = re.compile( r'\[([0-9a-z\-,]+)\]', re.VERBOSE )
143                nodenames = list()
144
145                for arg in args:
146                        found = findregex.findall( arg )
147                        ranges = list()
148
149                        if found:
150                                pattern = findregex.sub( '%s', arg )
151
152                                for part in found:
153                                        ranges.append( self.return_range( part ) )
154
155                                combs = self.combinations( ranges )
156
157                                for comb in combs:
158                                        # Here the %s in the pattern are
159                                        # replaced by the correct value
160                                        nodenames.append( pattern % comb )
161                        else:
162                                nodenames.append( arg )
163
164                return nodenames
165
166        def check_values(self, values, args):
167                '''
168                Here we override the default method in OptionParser to
169                enable our extra parsing on the given Arguments
170                '''
171                return values, self.args_parser( args )
172
173if __name__ == "__main__":
174        import doctest
175        doctest.testmod()
Note: See TracBrowser for help on using the repository browser.