Attachment 'PyBurlap.py'
Download
Toggle line numbers
1 #
2 # A Burlap client interface for Python. The date and long types require
3 # Python 2.3 or later.
4 #
5 # The Burlap proxy is used as follows:
6 #
7 # proxy = Burlap("http://localhost:8080/buffalo/Hello")
8 #
9 # print proxy.hello()
10 #
11 import string, time
12 import urllib
13 #import xmllib
14 from types import *
15
16 __version__ = "0.1"
17
18
19 # --------------------------------------------------------------------
20 # Exceptions
21
22 class Error:
23 # base class for client errors
24 pass
25
26 class ProtocolError(Error):
27 # Represents an HTTP protocol error
28 def __init__(self, url, code, message, headers):
29 self.url = url
30 self.code = code
31 self.message = message
32 self.headers = headers
33
34 def __repr__(self):
35 return (
36 "<ProtocolError for %s: %s %s>" %
37 (self.url, self.code, self.message)
38 )
39
40 class Fault(Error):
41 # Represents a fault from Burlap
42 def __init__(self, code, message, **detail):
43 self.code = code
44 self.message = message
45
46 def __repr__(self):
47 return "<BurlapFault %s: %s>" % (self.code, self.message)
48
49 # --------------------------------------------------------------------
50 # Wrappers for Burlap data types non-standard in Python
51 #
52
53 #
54 # Boolean -- use the True or False constants
55 #
56 class Boolean:
57 def __init__(self, value = 0):
58 self.value = (value != 0)
59
60 def _burlap_write(self, out):
61 if self.value:
62 out.write('<boolean>')
63 out.write('1')
64 out.write('</boolean>')
65 else:
66 out.write('<boolean>')
67 out.write('0')
68 out.write('</boolean>')
69
70 def __repr__(self):
71 if self.value:
72 return "<True at %x>" % id(self)
73 else:
74 return "<False at %x>" % id(self)
75
76 def __int__(self):
77 return self.value
78
79 def __nonzero__(self):
80 return self.value
81
82 True, False = Boolean(1), Boolean(0)
83
84 #
85 # Date - wraps a time value in seconds
86 #
87 class Date:
88 def __init__(self, value = 0):
89 self.value = value
90
91 def __repr__(self):
92 return ("<Date %s at %x>" %
93 (time.asctime(time.localtime(self.value)), id(self)))
94
95 def _burlap_write(self, out):
96 out.write("<date>")
97 out.write(self.value)
98 out.write("</date>")
99 #
100 # Binary - binary data
101 #
102
103 class Binary:
104 def __init__(self, data=None):
105 self.data = data
106
107 def _burlap_write(self, out):
108 out.write('<base64>')
109 out.write(self.data)
110 out.write('</base64>')
111
112 #
113 # BurlapWriter - writes Burlap data from Python objects
114 #
115 class BurlapWriter:
116 dispatch = {}
117
118 def write_call(self, method, params):
119 self.refs = {}
120 self.ref = 0
121 self.__out = []
122 self.write = write = self.__out.append
123
124 write("<burlap:call>");
125 #write(pack(">H", len(method)));
126 write("<method>")
127 write(method);
128 write("</method>")
129 for v in params:
130 self.write_object(v)
131 write("</burlap:call>");
132 result = string.join(self.__out, "")
133 del self.__out, self.write, self.refs
134 return result
135
136 def write_object(self, value):
137 try:
138 f = self.dispatch[type(value)]
139 except KeyError:
140 raise TypeError, "cannot write %s objects" % type(value)
141 else:
142 f(self, value)
143
144 def write_int(self, value):
145 self.write('<int>')
146 self.write(str(value))
147 self.write('</int>')
148 dispatch[IntType] = write_int
149
150 def write_long(self, value):
151 self.write('<long>')
152 self.write(str(value))
153 self.write('</long>')
154 dispatch[LongType] = write_long
155
156 def write_double(self, value):
157 self.write('<double>')
158 self.write(str(value))
159 self.write('</double>')
160 dispatch[FloatType] = write_double
161
162 def write_string(self, value):
163 self.write('<string>')
164 self.write(str(value))
165 self.write('</string>')
166 dispatch[StringType] = write_string
167
168 def write_reference(self, value):
169 # check for and write circular references
170 # returns 1 if the object should be written, i.e. not a reference
171 i = id(value)
172 if self.refs.has_key(i):
173 self.write('<ref>')
174 self.write(self.refs[i])
175 self.write('</ref>')
176 return 0
177 else:
178 self.refs[i] = self.ref
179 self.ref = self.ref + 1
180 return 1
181
182 def write_list(self, value):
183 if self.write_reference(value):
184 self.write("<list>");
185 self.write('<length>'+str(len(value))+'</length>')
186 for v in value:
187 #print v
188 self.write_object(v)
189 self.write('</list>')
190 dispatch[TupleType] = write_list
191 dispatch[ListType] = write_list
192
193 def write_map(self, value):
194 if self.write_reference(value):
195 self.write("<map>")
196 for k, v in value.items():
197 #self.__write(k)
198 #self.__write('<string>'+v+'</string>')
199 self.write_object(v)
200 self.write_object(k)
201 self.write("</map>")
202 dispatch[DictType] = write_map
203
204 def write_instance(self, value):
205 # check for special wrappers
206 if hasattr(value, "_burlap_write"):
207 value._burlap_write(self)
208 dispatch[InstanceType] = write_instance
209
210 from xml.parsers import expat
211 class Parser:
212 def __init__(self):
213 self._parser = expat.ParserCreate()
214 self._parser.StartElementHandler = self.start
215 #self._parser.EndElementHandler = self.end
216 self._parser.CharacterDataHandler = self.data
217 self.tags = []
218 self.datas = []
219
220 def feed(self, data):
221 self._parser.Parse(data, 0)
222
223 def close(self):
224 self._parser.Parse("", 1) # end of data
225 del self._parser # get rid of circular references
226
227 def start(self, tag, attrs):
228 #print "START", repr(tag), attrs
229 self.tags.append(tag)
230
231 def end(self, tag):
232 print "END", repr(tag)
233
234 def data(self, data):
235 #print "DATA", repr(data)
236 self.datas.append(data)
237
238
239 #
240 # Parses the results from the server
241 #
242 class BurlapParser:
243 def __init__(self, f):
244 self._f = f
245 self.read = self._f.readline()
246 self._refs = []
247
248 def parse_reply(self):
249 #read = self.read
250 #major = read(1)
251 #minor = read(1)
252 #ch = read(1)
253
254 value = self.parse_object()
255
256 return value
257 #self.error() # actually a fault
258
259 def parse_object(self):
260 # parse an arbitrary object based on the type in the data
261 return self.parse_object_code(self.read)
262
263 def parse_object_code(self, code):
264 # parse an object when the code is known
265 read = self.read
266
267 p = Parser()
268 p.feed(read)
269 p.close()
270
271 if code == 'null':
272 return None
273
274 elif p.tags[1] == 'boolean':
275 return p.datas[0]
276
277 #elif code == 'F':
278 # return False
279
280 elif p.tags[1] == 'int':
281 return p.datas[0]
282
283 elif p.tags[1] == 'long':
284 return p.datas[0]
285
286 elif p.tags[1] == 'double':
287 return p.datas[0]
288
289 elif p.tags[1] == 'date':
290 ms = p.datas[0]
291
292 return ms
293
294 elif p.tags[1] == 'string':
295 #return self.parse_string()
296 return p.datas[0]
297
298 elif code == 'B':
299 return Binary(self.parse_string())
300
301 elif p.tags[1] == 'list':
302 list = []
303 self._refs.append(list)
304 i = 2#skip length
305 while i<=len(p.datas):
306 list.append(p.datas[i-1])
307 i = i+1
308 return list
309
310 elif p.tags[1] == 'map':
311 #self.parse_type() # skip type
312 map = {}
313 #self._refs.append(map)
314 i = 1
315 while i<=len(p.datas):
316 #key = self.parse_object_code(ch)
317 #value = self.parse_object()
318 key = p.datas[i-1]
319 value = p.datas[i]
320 map[key] = value
321 #ch = read(1)
322 i = i+2
323 return map
324
325 elif code == 'R':
326 return self._refs[unpack('>l', read(4))[0]]
327
328 elif code == 'r':
329 self.parse_type() # skip type
330 url = self.parse_type() # reads the url
331 return Burlap(url)
332
333 else:
334 raise "UnknownObjectCode %d" % code
335
336 def parse_type(self):
337 f = self._f
338 len = unpack('>cH', f.read(3))[1]
339 return f.read(len)
340
341 def error(self):
342 raise "FOO"
343
344 #
345 # Encapsulates the method to be called
346 #
347 class _Method:
348 def __init__(self, invoker, method):
349 self._invoker = invoker
350 self._method = method
351
352 def __call__(self, *args):
353 return self._invoker(self._method, args)
354
355 # --------------------------------------------------------------------
356 # Burlap is the main class. A Burlap proxy is created with the URL
357 # and then called just as for a local method
358 #
359 # proxy = Burlap("http://localhost:8080/buffalo/Hello")
360 # print proxy.hello()
361 #
362 class Burlap:
363 """Represents a remote object reachable by Burlap"""
364
365 def __init__(self, url):
366 # Creates a Burlap proxy object
367
368 self._url = url
369
370 # get the uri
371 type, uri = urllib.splittype(url)
372 if type != "http":
373 raise IOError, "unsupported Burlap protocol"
374
375 self._host, self._uri = urllib.splithost(uri)
376
377 def __invoke(self, method, params):
378 # call a method on the remote server
379
380 request = BurlapWriter().write_call(method, params)
381
382 import httplib
383
384 h = httplib.HTTP(self._host)
385 h.putrequest("POST", self._uri)
386
387 # required by HTTP/1.1
388 h.putheader("Host", self._host)
389
390 h.putheader("User-Agent", "PyBurlap.py/%s" % __version__)
391 h.putheader("Content-Length", str(len(request)))
392
393 h.endheaders()
394
395 h.send(request)
396
397 errcode, errmsg, headers = h.getreply()
398
399 if errcode != 200:
400 raise ProtocolError(self._url, errcode, errmsg, headers)
401
402 return self.parse_response(h.getfile())
403
404 def parse_response(self, f):
405 # read response from input file, and parse it
406
407 parser = BurlapParser(f)
408 value = parser.parse_reply()
409 f.close()
410
411 return value
412
413 def _hessian_write(self, out):
414 # marshals the proxy itself
415 out.write("rt\x00\x00S")
416 out.write(pack(">H", len(self._url)))
417 out.write(self._url)
418
419 def __repr__(self):
420 return "<BurlapProxy %s>" % self._url
421
422 __str__ = __repr__
423
424 def __getattr__(self, name):
425 # encapsulate the method call
426 return _Method(self.__invoke, name)
427
428 #
429 # Testing code.
430 #
431 if __name__ == "__main__":
432 proxy = Burlap("http://localhost:8080/buffalo/Hello")
433
434 try:
435 print proxy.hello("Breeze")
436 print proxy.replyInt(10)
437 print proxy.replyLong(10000)
438 print proxy.replyDouble(1000000.90)
439 print proxy.callIntStr(100,"Breeze")
440
441 print proxy.replyList(['Breeze','QingFeng','Wind',100,300.68])#list
442
443 print proxy.replyMap({'Breeze':1,'QingFeng':2,'Wind':3,100:4,300.68:5})#map->Python is dict
444
445 print proxy.replyBoolean(True)
446
447 #ISO_8609_DATETIME = '%Y%m%dT%H%M%SZ'
448 print proxy.replyDate(Date('20050501T095231Z'))
449
450 import base64
451 print proxy.replyBase64(base64.encodestring('Breeze Base64'))
452 except Error, v:
453 print "ERROR", v
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.