23d04ed9b0939ab84df4ea5ebf919e172d02429d
[tx] / xpath_prompt.py
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 # XPath prompt
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 import re
22 import sys
23 import time
24 import urllib
25 import StringIO
26
27 import htmltree
28 from xpath import XPath
29 import xpathparser
30 from sequence import Sequence
31 from nodes import Node, Document
32 from error import Error
33 from parser import NoMatch
34 from sequence_misc import printSequence
35 from xpathfn import *
36
37 from xpath_misc import lispy
38
39 g_defaultUserAgent = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
40
41 def printInlineSequence( sequence ) :
42
43         for item in sequence :
44                 if isinstance( item , Node ) :
45                         item.serialize( file = sys.stdout )
46                         print
47                 else :
48                         print item
49
50 def readDoc( uri ) :
51
52         txt = urllib.urlopen( uri ).read()
53         return htmltree.parse( txt )
54
55 def resetUserAgent( env ) :
56
57         ua = env.get( 'ua' )
58         if len( ua ) == 1 :
59                 ua = ua[ 0 ]
60                 if isinstance( ua , basestring ) :
61                         urllib.URLopener.version = ua
62
63 def extSerialize( context ) :
64
65         return Sequence( context.item.serialize() )
66
67 def extParse( context , text ) :
68
69         return Sequence( htmltree.parse( text[ 0 ] ) )
70
71 def fnDoc( context , uri , _cache = {} ) :
72
73         uri = uri[ 0 ]
74         if uri not in _cache :
75                 try :
76                         doc = readDoc( uri )
77                 except :
78                         raise
79                         print 'Error reading URI %s' % uri
80                         return ()
81                 _cache[ uri ] = Sequence( doc )
82         return _cache[ uri ]
83
84 def extSort( context , arg ) :
85
86         return Sequence( sorted( arg ) )
87
88 def evaluate( expr , dot , env , functions ) :
89
90         #
91         # Compile the expression
92         #
93         t = time.time()
94         x = XPath( expr )
95         dp = time.time() - t
96
97         #
98         # Evaluate the compiled expression
99         #
100         t = time.time()
101         r = x.eval( dot , env , functions )
102         de = time.time() - t
103
104         return r , dp , de
105
106 def printResult( sequence , mode , displayLocation ) :
107
108         if mode == 'inline' :
109                 printInlineSequence( sequence )
110         elif mode == 'full' :
111                 printSequence( sequence , True , displayLocation )
112         elif mode == 'short' :
113                 printSequence( sequence , False , displayLocation )
114         else : # assuming default mode
115                 if len( sequence ) == 0 :
116                         pass
117                 elif len( sequence ) == 1 :
118                         print sequence[ 0 ]
119                 else :
120                         print sequence
121
122 help = r'''
123 <expression>          Evaluate XPath expression.
124 $var := <expression>  Evaluate XPath expression and store result in 'var'.
125
126 Commands:
127
128 \. URI         Load document from URI and set it as the current node.
129 \?             Print this help.
130 \c             Flush cache.
131 \d             Default mode.
132 \e EXPRESSION  Display XPath parser result
133 \f             Full mode.
134 \i             Inline mode.
135 \l             Toggle location display in full and short mode.
136 \o             Switch optimization on/off.
137 \s             Short mode.
138 \v             Print name of available variables.
139 \x             Switch timer on/off.
140 '''.strip()
141
142 def main() :
143
144         import readline
145         import re
146
147         reDef = re.compile( r'^\s*\$([a-z_][a-z_0-9-]*)\s*:=\s*(.*)$' , re.I )
148
149         doc = Document()
150
151         mode = 'default'
152
153         modes = {
154                 'd' : 'default' ,
155                 'f' : 'full' ,
156                 's' : 'short' ,
157                 'i' : 'inline'
158                 }
159
160         env = {
161                 'current' : doc ,
162                 'version' : Sequence( 'TX' , '0.1' ) ,
163                 'ua'      : Sequence( g_defaultUserAgent )
164                 }
165
166         functions = {
167                 'doc'       : fnDoc ,
168                 'parse'     : extParse ,
169                 'serialize' : extSerialize ,
170                 'sort'      : extSort
171                 }
172
173         print 'XPath TX 0.1 - (c)2005  Frederic Jolliton <frederic@jolliton.com>\n'
174         print 'Use \? for help.\n'
175
176         displayLocation = False
177         showTime = False
178         while 1 :
179                 try :
180                         line = raw_input( 'XPath2.0> ' )
181                 except EOFError :
182                         print
183                         break
184                 except KeyboardInterrupt :
185                         print
186                         break
187                 try :
188                         line = line.decode( sys.stdin.encoding )
189                 except :
190                         print 'Error decoding input with encoding %r. Using raw input.' % ( sys.stdin.encoding , )
191                 line = line.strip()
192                 if line.startswith( '#' ) or not line :
193                         pass
194                 elif line.startswith( '\\' ) :
195                         resetUserAgent( env )
196                         cmd = line[ 1 : ]
197                         if cmd in modes :
198                                 mode = modes[ cmd ]
199                                 print '[%s]' % mode
200                         elif cmd.startswith( '. ' ) :
201                                 uri = cmd[ 2 : ].strip()
202                                 try :
203                                         env[ 'current' ] = readDoc( uri )
204                                 except :
205                                         print 'Error reading URI %r' % uri
206                         elif cmd.startswith( 'e ' ) :
207                                 try :
208                                         print lispy( xpathparser.parse( cmd[ 1 : ].strip() ) )
209                                 except NoMatch , e :
210                                         print e
211                         elif cmd == 'c' :
212                                 import xpath
213                                 xpath.flushCache()
214                                 print 'Cache flushed'
215                         elif cmd == 'o' :
216                                 import xpath
217                                 xpath.g_dontOptimize = not xpath.g_dontOptimize
218                                 print 'Optimization turned' , ('off','on')[ not xpath.g_dontOptimize ]
219                         elif cmd == 'l' :
220                                 displayLocation = not displayLocation
221                                 print 'Location' , ('off','on')[ displayLocation ]
222                         elif cmd == 'x' :
223                                 showTime = not showTime
224                                 print 'Timer' , ('off','on')[ showTime ]
225                         elif cmd == 'v' :
226                                 print '$' + ', $'.join( sorted( env.keys() ) )
227                         elif cmd == '?' :
228                                 print help
229                                 print
230                                 print 'Current mode is %r' % mode
231                                 print 'Location is' , ('off','on')[ displayLocation ]
232                                 print 'Timer is' , ('off','on')[ showTime ]
233                         else :
234                                 print 'Unknown command %r' % cmd
235                 else :
236                         r = reDef.match( line )
237                         if r is not None :
238                                 varName , line = r.groups()
239                         else :
240                                 varName = None
241                         resetUserAgent( env )
242                         try :
243                                 dot = env[ 'current' ]
244                                 if isinstance( dot , Sequence ) :
245                                         assert len( dot ) == 1 , 'expected a sequence of 1 item'
246                                         dot = dot[ 0 ]
247                                 try :
248                                         result , dp , de = evaluate( line , dot , env , functions )
249                                 except KeyboardInterrupt :
250                                         print '[Interrupted]'
251                                 else :
252                                         env[ '_' ] = result
253                                         if varName is not None :
254                                                 env[ varName ] = result
255                                         else :
256                                                 try :
257                                                         printResult( result , mode , displayLocation )
258                                                 except KeyboardInterrupt : # FIXME: Don't work.
259                                                         print '[Interrupted]'
260                                         if showTime :
261                                                 print '-- %fs(parse) + %fs(eval) --' % ( dp , de )
262                         except KeyboardInterrupt :
263                                 raise
264                         except NoMatch , e :
265                                 print e
266                         except XPathError , e :
267                                 print 'XPATH-ERROR [%s]' % ( e , )
268                         except Error , e :
269                                 print 'GENERIC-ERROR [%s]' % ( e , )
270         print 'bye'
271
272 if __name__ == '__main__' :
273         main()
274
275 # Local Variables:
276 # tab-width: 4
277 # python-indent: 4
278 # End: