source: trunk/src/AdvancedParser.py @ 210

Last change on this file since 210 was 181, checked in by dennis, 14 years ago

AdvancedParser?:

  • Improved error handling
File size: 6.2 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 3667 2009-04-15 12:02:04Z dennis $
10#       $URL: https://subtrac.sara.nl/hpcv/svn/beowulf/trunk/sara_python_modules/AdvancedParser.py $
11#
12from optparse import OptionParser
13import re
14import types
15
16__author__  = "Dennis Stam"
17__version__ = ( 1, 1, 0 )
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                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
163if __name__ == "__main__":
164        import doctest
165        doctest.testmod()
Note: See TracBrowser for help on using the repository browser.