Added Node.valueOf. Added deferred function inside XML tree. Fixed quotes bug.
authorFrederic Jolliton <frederic@jolliton.com>
Mon, 14 Nov 2005 04:16:59 +0000 (04:16 +0000)
committerFrederic Jolliton <frederic@jolliton.com>
Mon, 14 Nov 2005 04:16:59 +0000 (04:16 +0000)
 * Added Node.valueOf which return the first item of the sequence
 returned by the xpath expression as a string.

 * Added deferred function, which is a ProcessingInstruction node that
 call a function at serialization time. This function take no parameters,
 and should return a valid XML tree fragment.

 * reCharacterToQuoteForText was defined 2 times with different values,
 causing streamXmlQuoteText to work incorrectly (suppressing ' and " from
 text string.)
git-archimport-id: frederic@jolliton.com--2005-main/tx--main--0.1--patch-49

nodes.py
tags.py

index c66dac0..4701490 100644 (file)
--- a/nodes.py
+++ b/nodes.py
@@ -37,6 +37,17 @@ from misc import shortenText, typeOf, stringValuesCompare
 
 # IMPORTANT: Additionnal import at end of module
 
+def iterFlatten( sequence ) :
+
+       if sequence is None :
+               pass
+       elif isinstance( sequence , basestring ) or isinstance( sequence , Node ) :
+               yield sequence
+       else :
+               for item in sequence :
+                       for sub in iterFlatten( item ) :
+                               yield sub
+
 def someDuplicates( items , getValue = id ) :
 
        '''Predicate to test if there is duplicate in 'items'.'''
@@ -122,6 +133,8 @@ def streamXmlQuoteText( s , prt ) :
                        prt( '&amp;' )
                elif s[ e ] == '<' :
                        prt( '&lt;' )
+               else :
+                       raise NotReached
        prt( s[ p : ] )
 
 def xmlQuoteAttribute( s ) :
@@ -135,7 +148,7 @@ def xmlQuoteAttribute( s ) :
                .replace( '"' , '&quot;' ) \
                .replace( "'" , '&apos;' )
 
-reCharacterToQuoteForText = re.compile( '''[&<"']''' )
+reCharacterToQuoteForAttribute = re.compile( '''[&<"']''' )
 def streamXmlQuoteAttribute( s , prt ) :
 
        '''Feed string 's' to 'prt' with &, <, " and ' characters translated
@@ -143,7 +156,7 @@ def streamXmlQuoteAttribute( s , prt ) :
 
        p = 0
        while 1 :
-               r = reCharacterToQuoteForText.search( s , p )
+               r = reCharacterToQuoteForAttribute.search( s , p )
                if r is None :
                        break
                e = r.start( 0 )
@@ -157,6 +170,8 @@ def streamXmlQuoteAttribute( s , prt ) :
                        prt( '&quot;' )
                elif s[ e ] == "'" :
                        prt( '&apos;' )
+               else :
+                       raise NotReached
        prt( s[ p : ] )
 
 #----------------------------------------------------------------------------
@@ -302,6 +317,17 @@ class Node( object ) :
 
                raise NotImplementedError
 
+       def valueOf( self , path ) :
+
+               sequence = self.xpath( path )
+               if len( sequence ) == 0 :
+                       return None
+               else :
+                       item = sequence[ 0 ]
+                       if isinstance( item , Node ) :
+                               item = item.dmStringValue()
+                       return item
+
        def match( self , pat ) :
 
                import pattern
@@ -787,7 +813,7 @@ class Attribute( Node ) :
 
 class ProcessingInstruction( Node ) :
 
-       __slots__ = [ 'target' , 'content' ]
+       __slots__ = [ 'target' , 'content' , '_function' ]
 
        def __init__( self , target , content = '' ) :
 
@@ -799,6 +825,7 @@ class ProcessingInstruction( Node ) :
                        raise NodeError( "'?>' must not occur inside content of processing instruction." )
                self.target = target
                self.content = content
+               self._function = None
 
        def iterStringValue( self ) :
 
@@ -841,12 +868,18 @@ class ProcessingInstruction( Node ) :
                assert self.target.lower() != 'xml' , '"xml" not allowed as PI target name'
                assert '?>' not in self.content , '"?>" not allowed inside PI contents'
 
-               prt( '<?' )
-               prt( self.target )
-               if self.content :
-                       prt( ' ' )
-                       prt( self.content )
-               prt( '?>' )
+               if callable( self._function ) :
+                       # If self._function is callable, it's called, and the XML
+                       # tree returned by it is serialized instead.
+                       for item in iterFlatten( self._function() ) :
+                               item._serialize( prt )
+               else :
+                       prt( '<?' )
+                       prt( self.target )
+                       if self.content :
+                               prt( ' ' )
+                               prt( self.content )
+                       prt( '?>' )
 
        def _asDebug( self , context ) :
 
diff --git a/tags.py b/tags.py
index e29385e..0d88516 100644 (file)
--- a/tags.py
+++ b/tags.py
@@ -52,6 +52,7 @@ def ensureNode( node ) :
        if isinstance( node , Node ) :
                return node
        elif isinstance( node , basestring ) :
+               print node
                return Text( node )
        else :
                raise Error( 'Invalid expression of type %s to build tree' % typeOf( node ) )
@@ -60,7 +61,7 @@ def iterFlatten( sequence ) :
 
        if sequence is None :
                pass
-       elif isinstance( sequence , basestring ) or isinstance( sequence , Node ) :
+       elif isinstance( sequence , ( basestring , Node ) ) or callable( sequence ) :
                yield sequence
        else :
                for item in sequence :
@@ -73,6 +74,11 @@ class Tags :
 
                '''Create functions on the fly as needed.'''
 
+               def deferredFunction( function ) :
+                       pi = ProcessingInstruction( 'net.tuxee.tx' , `function` )
+                       pi._function = function
+                       return pi
+
                if name == '_doc_' :
                        def fun( *contents ) :
                                children = [ ensureNode( child ) for child in iterFlatten( contents ) if child ]
@@ -95,9 +101,11 @@ class Tags :
                                attributes = [ Attribute( n , v ) for n , v in kws( kwargs ).items() ]
                                children = []
                                for item in iterFlatten( contents ) :
-                                       if isinstance( item , Attribute ) :
+                                       if callable( item ) :
+                                               children.append( deferredFunction( item ) )
+                                       elif isinstance( item , Attribute ) :
                                                attributes.append( item )
-                                       elif item :
+                                       elif item is not None :
                                                children.append( ensureNode( item ) )
                                        else :
                                                # discard