Updated install script to store current Arch project version into a log file.
[confparser-old] / confparser_ext.py
1 # -*- coding: utf-8 -*-
2
3 #
4 # Configuration parser extension
5 # Copyright (C) 2005  Frédéric Jolliton <frederic@jolliton.com>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 #
21
22 #
23 # This module is sort of prototype to support xpath-like
24 # way to fetch certain nodes from a configuration tree.
25 #
26 # Code is absolutely not nice.. It's really just for experimentation
27 # and need to be rewritten completely.
28 #
29
30 import re
31
32 def flatten( lst ) :
33
34         '''Flatten a list or tuple.
35         
36         [[1,2],[3,[4,5]]] => [1,2,3,4,5]'''
37
38         if isinstance( lst , list ) :
39                 r = []
40                 for item in lst :
41                         r += flatten( item )
42         elif isinstance( lst , tuple ) :
43                 r = ()
44                 for item in lst :
45                         r += tuple( flatten( item ) )
46         else :
47                 r = ( lst , )
48         return r
49
50 def quote( s ) :
51
52         s = str( s )
53         if re.search( r'^(?:\d+|[a-z_][a-z0-9_-]*)$' , s , re.I ) is None :
54                 s = '"%s"' % s.replace( '\\' , '\\\\' ).replace( '"' , '\\"' )
55         return s
56
57 class NodeSet :
58
59         def __init__( self , *nodes ) :
60
61                 self.__nodes = []
62                 self.__iadd__( flatten( nodes ) )
63
64         def __iadd__( self , *others ) :
65
66                 others = flatten( others )
67                 for item in others :
68                         assert isinstance( item , ( Node , NodeSet ) )
69                 for item in others :
70                         if isinstance( item , NodeSet ) :
71                                 self.__nodes += item.__nodes
72                         else :
73                                 self.__nodes.append( item )
74                 return self
75
76         def __getitem__( self , place ) :
77
78                 if isinstance( place , str ) :
79                         return select( self , place )
80                 return self.__nodes[ place ]
81
82         def __len__( self ) :
83
84                 return len( self.__nodes )
85
86         def pstr( self , indent = '' ) :
87
88                 return '\n'.join( [ node.pstr( indent ) for node in self.__nodes ] )
89
90         def __repr__( self ) :
91
92                 return '<NodeSet with %d elements>' % len( self.__nodes )
93
94 class Node :
95
96         def __init__( self , name , *values ) :
97
98                 self.__name = name
99                 self.__values = flatten( values )
100                 self.__subs = NodeSet()
101
102         def __iadd__( self , *others ) :
103
104                 self.__subs += others
105                 return self
106
107         def __getName( self ) :
108
109                 return self.__name
110
111         name = property( __getName )
112
113         def __getSubs( self ) :
114
115                 return self.__subs
116
117         subs = property( __getSubs )
118
119         def __getValues( self ) :
120
121                 return self.__values
122
123         values = property( __getValues )
124
125         def pstr( self , indent = '' ) :
126
127                 r = indent + quote( self.__name ) + ' '
128                 if self.__values :
129                         r += ' '.join( map( quote , self.__values ) ) + ' '
130                 if len( self.__subs ) != 0 :
131                         r += '{\n' + self.__subs.pstr( indent + '  ' ) + '\n' + indent + '}'
132                 else :
133                         r += ';'
134                 return r
135
136         def __getitem__( self , place ) :
137
138                 if isinstance( place , str ) :
139                         return select( self , place )
140                 return self.subs[ place ]
141
142         def __repr__( self ) :
143
144                 return '<Node :name %r>' % ( self.__name , )
145
146 def confToNodeset( node ) :
147
148         def confToNodeset_( node ) :
149
150                 if isinstance( node , tuple ) :
151                         result = Node( node[ 0 ] , node[ 1 ] or [] )
152                         result += confToNodeset_( node[ 2 ] )
153                 elif isinstance( node , list ) :
154                         result = NodeSet( [ confToNodeset_( sub ) for sub in node or () ] )
155                 else :
156                         result = None
157                 return result
158
159         return NodeSet( confToNodeset_( node ) )
160
161 #
162 # FIXME: UGLY
163 #
164 def select( ns , path ) :
165
166         def select_( ns , path ) :
167
168                 result = NodeSet( ns )
169                 while path :
170                         ns = result
171                         recPath = path
172                         part , path = path[ 0 ] , path[ 1 : ]
173                         recurse = False
174                         if part == '' :
175                                 recurse = True
176                                 part , path = path[ 0 ] , path[ 1 : ]
177                         #
178                         # Build predicates
179                         #
180                         if ':' in part :
181                                 part = part.split( ':' )
182                                 def matcher( sub ) :
183                                         result = ( part[ 0 ] in [ sub.name , '*' ] and len( sub.values ) == len( part ) - 1 )
184                                         if result :
185                                                 for v1 , v2 in zip( sub.values , part[ 1 : ] ) :
186                                                         if re.match( v2
187                                                                 .replace( '.' , '\\.' )
188                                                                 .replace( '*' , '.*' )
189                                                                 .replace( '?' , '. ') , v1 , re.I ) is None :
190                                                                 result = False
191                                                                 break
192                                         return result
193                         else :
194                                 def matcher( sub ) :
195                                         return part in [ sub.name , '*' ]
196                         #
197                         # Select subnodes according to the predicate
198                         #
199                         result = NodeSet()
200                         for item in ns :
201                                 for sub in item.subs :
202                                         if matcher( sub ) :
203                                                 result += sub
204                                         if recurse :
205                                                 result += select_( sub , recPath )
206                 return result
207
208         path = path.split( '/' )
209         if path and path[ 0 ] == '' :
210                 # assume // at beginning.. BAD BAD BAD
211                 del path[ 0 ]
212         if not path :
213                 result = ns
214         else :
215                 if path[ -1 ] == '@@' :
216                         def finally_( result ) :
217                                 return [ node.values for node in result ]
218                         path.pop()
219                 elif path[ -1 ] == '@' :
220                         def finally_( result ) :
221                                 return sum( [ node.values for node in result ] , () )
222                         path.pop()
223                 elif path[ -1 ] == '?' :
224                         def finally_( result ) :
225                                 if len( result ) :
226                                         return ' '.join( [ str( s ) for node in result for s in node.values ] )
227                         path.pop()
228                 else :
229                         def finally_( result ) :
230                                 return result
231                 result = select_( ns , path )
232                 result = finally_( result )
233
234         return result
235
236 def test() :
237
238         filename = 'vserver.conf'
239
240         import confparser
241         n = confparser.readConfigurationExt( filename )
242         if n is not None :
243                 print n[ 'default/unhide' ].pstr()
244                 print '--'
245                 print n[ 'default/unhide' ] # the nodeset
246                 print n[ 'default/unhide/?' ] # the string for the whole nodesets
247                 print n[ 'default/unhide/@' ] # the flatten list of text
248                 print n[ 'default/unhide/@@' ] # the list of list of text
249                 print n[ 'server/@' ]
250                 print n[ 'server:m*/rootdev/@' ]
251                 print n[ '*/@@' ]
252                 print n[ '//*/@@' ]
253         else :
254                 print 'Unable to parse %r' % filename
255
256 if __name__ == '__main__' :
257         test()
258
259 # Local Variables:
260 # tab-width: 4
261 # python-indent: 4
262 # End: