Tags now allow nested tuple/list, including iterators, to create nodes.
[tx] / tags.py
1 # -*- coding:utf-8 -*-
2
3 # Tags
4 # Copyright (C) 2005  Frédéric Jolliton <frederic@jolliton.com>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 __all__ = [ 'Tags' , 'tags' ]
21
22 from nodes import *
23 from error import *
24 from misc import typeOf
25
26 def decodeName( name ) :
27
28         '''
29         Decode python variable name.  If '_' is the first character, it's
30         discarded.  Other '_' are converted to '-', except for 2 '__' that
31         are converted to ':'.
32
33         '_class' -> 'class'
34         'xml__lang' -> 'xml:lang'
35         'http_equiv' -> 'http-equiv'
36         '''
37
38         if name.startswith( '_' ) and len( name ) > 1 :
39                 name = name[ 1 : ]
40         name = name.replace( '__' , ':' )
41         name = name.replace( '_' , '-' )
42         return name
43
44 def kws( kwargs ) :
45
46         '''Copy 'kwargs' dictionnary, rewriting keys using decodeName().'''
47
48         return dict( [ ( decodeName( key ) , value ) for key , value in kwargs.items() if value is not None ] )
49
50 def ensureNode( node ) :
51
52         if isinstance( node , Node ) :
53                 return node
54         elif isinstance( node , basestring ) :
55                 return Text( node )
56         else :
57                 raise Error( 'Invalid expression of type %s to build tree' % typeOf( node ) )
58
59 def iterFlatten( sequence ) :
60
61         if isinstance( sequence , basestring ) or isinstance( sequence , Node ) :
62                 yield sequence
63         else :
64                 for item in sequence :
65                         for sub in iterFlatten( item ) :
66                                 yield sub
67
68 class Tags :
69
70         def __getattr__( self , name ) :
71
72                 '''Create functions on the fly as needed.'''
73
74                 if name == '_doc_' :
75                         def fun( *contents ) :
76                                 children = [ ensureNode( child ) for child in iterFlatten( contents ) if child ]
77                                 return Document( children )
78                 elif name == '_comment_' :
79                         def fun( contents ) :
80                                 return Comment( contents )
81                 elif name == '_pi_' :
82                         def fun( target , contents ) :
83                                 return ProcessingInstruction( target , contents )
84                 else :
85                         def fun( *contents , **kwargs ) :
86
87                                 '''If first item of contents is a dictionnary, then its items
88                                 take precedence on kwargs items.'''
89
90                                 if contents and isinstance( contents[ 0 ] , dict ) :
91                                         k , contents = contents[ 0 ] , contents[ 1 : ]
92                                         kwargs.update( k )
93                                 attributes = [ Attribute( n , v ) for n , v in kws( kwargs ).items() ]
94                                 children = []
95                                 for item in iterFlatten( contents ) :
96                                         if isinstance( item , Attribute ) :
97                                                 attributes.append( item )
98                                         elif item :
99                                                 children.append( ensureNode( item ) )
100                                         else :
101                                                 # discard
102                                                 pass
103                                 return Element( decodeName( name ) , attributes , children )
104
105                 self.__dict__[ name ] = fun
106
107                 return fun
108
109 tags = Tags()
110
111 # Local Variables:
112 # tab-width: 4
113 # python-indent: 4
114 # End: