Package SPyRO :: Module formats
[hide private]
[frames] | no frames]

Source Code for Module SPyRO.formats

  1  #coding: iso-8859-1; 
  2  ############################################################################ 
  3  #                                                                          # 
  4  #    This file 'formats.py'                                                  # 
  5  #    is part of 'SPyRO: Simple Python Remote Objects'                      # 
  6  #    Copyright (C) 2004-2005 by Eric Sadit Tellez Avila                    # 
  7  #    Copyright (C) 2005-2006 by Eric Sadit Tellez Avila                    # 
  8  #    sadit@lsc.fie.umich.mx or donsadit@gmail.com                          # 
  9  #                                                                          # 
 10  #    This program is free software; you can redistribute it and#or modify  # 
 11  #    it under the terms of the GNU General Public License as published by  # 
 12  #    the Free Software Foundation; either version 2 of the License, or     # 
 13  #    (at your option) any later version.                                   # 
 14  #                                                                          # 
 15  #    This program is distributed in the hope that it will be useful,       # 
 16  #    but WITHOUT ANY WARRANTY; without even the implied warranty of        # 
 17  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         # 
 18  #    GNU General Public License for more details.                          # 
 19  #                                                                          # 
 20  #    You should have received a copy of the GNU General Public License     # 
 21  #    along with this program; if not, write to the                         # 
 22  #    Free Software Foundation, Inc.,                                       # 
 23  #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             # 
 24  ############################################################################ 
 25   
 26  import warn 
 27  warn = warn.Warn("SPyRO.formats") 
 28  import pickle 
 29  import cPickle 
 30  import marshal 
 31  import re 
 32  import SPyRO as spyro 
 33   
 34  import types 
 35  from types import DictType 
 36   
37 -def _set_booleans():
38 BooleanType = types.IntType 39 __builtins__['True'] = 1 40 __builtins__['False'] = 0
41 42 try: 43 BooleanType = types.BooleanType 44 True 45 except: 46 warn.warn("An old version of python or jython was detected") 47 _set_booleans() 48
49 -class SPyRO_Format:
50 """ The spyro format minimum requeriments """ 51 # General methods
52 - def contenttype(self):
53 """ Returns the content type to send menssages """ 54 return "application/xml"
55
56 - def _loads(self, msg):
57 """ Loads a msg. Its called for every load_* if the load_* 58 method is not overloaded """ 59 raise "loads must be implemented"
60
61 - def _dumps(self, msg):
62 """ Dumps a msg. Its called for every dump_* if the dump_* 63 method is not overloaded """ 64 raise "dumps must be implemented"
65 66 # Client side
67 - def dump_control(self, msg):
68 """ Creates a DEL|BYE request or any other request at low level """ 69 return self._dumps(msg)
70
71 - def dump_callMethod(self, msg):
72 """ Creates the call method """ 73 return self._dumps(msg)
74
75 - def dump_setAttr(self, msg):
76 """ Creates the set attribute message """ 77 return self._dumps(msg)
78
79 - def dump_getAttr(self, msg):
80 """ Creates the get attribute message """ 81 return self._dumps(msg)
82
83 - def load_result(self, msg):
84 return self._loads(msg)
85 86 ########### Server side
87 - def load_request(self, msg):
88 return self._loads(msg)
89
90 - def dump_result(self, msg):
91 """ Create a result message with |msg| object""" 92 return self._dumps(msg)
93
94 - def dump_error(self, msg):
95 """ Create a result message with |msg| object""" 96 return self._dumps(msg)
97
98 -class PickleFormat(SPyRO_Format):
99 """ This is the Interface to serialize (load and dumps) of the 100 pickle format broker. It is quite simple, and it just uses 101 the cPickle package. 102 """
103 - def __init__(self, commlevel):
104 """ Create a new pickle format manager """ 105 self.commlevel = commlevel
106
107 - def contenttype(self):
108 return "application/octet-stream"
109
110 - def _loads(self, msg):
111 """ Load received message, returns python data structures """ 112 #warn.debug("***************** READ1:", msg) 113 load = cPickle.loads(msg) 114 #load = pickle.loads(msg) 115 #warn.debug("***************** READ2:", load) 116 return load
117
118 - def _dumps(self, msg):
119 """ Dumps a message into data to send it across the network""" 120 # warn.debug( "***************** WRITE1:", msg ) 121 dump = cPickle.dumps(msg,self.commlevel) 122 # dump = pickle.dumps(msg,self.commlevel) 123 # warn.debug( "***************** WRITE2:", dump ) 124 return dump
125 126
127 -class MarshalWrapper(SPyRO_Format):
128 """ Calls a personalized marshaler with loads and dumps methods 129 MarshalWrapper envolves the with _loads and _dumps methods 130 """
131 - def contenttype(self):
132 return "application/octet-stream"
133
134 - def __init__(self, marshal):
135 self.marshal = marshal
136
137 - def _loads(self, msg):
138 return self.marshal.loads(msg)
139
140 - def _dumps(self, msg):
141 return self.marshal.dumps(msg)
142 143 144 145 # Registed formats 146 147 try: 148 import xml.marshal.generic 149 except: 150 warn.debug("Unable to use XML generic to communicate (with ", 151 "xml.marshal.generic module)") 152 try: 153 import xml.marshal.wddx 154 except: 155 warn.debug("Unable to use XML WDDX to communicate (with ", 156 "xml.marshal.wddx module)") 157 158 try: 159 import SOAPpy 160 _USE_SOAPpy = True 161 except: 162 _USE_SOAPpy = False 163 warn.debug("Unable to use SOAP to communicate (with SOAPpy module)") 164
165 -class SOAP_SOAPpy(SPyRO_Format):
166 """ 167 More info in /usr/lib/python2.4/site-packages/SOAPpy/SOAPBuilder.py 168 """
169 - def __init__(self):
170 pass
171
172 - def _dumps(self, msg):
173 #x = SOAPpy.buildSOAP(msg,method="SPyRO_execute") 174 x = SOAPpy.buildSOAP(msg) 175 #print x 176 return x
177
178 - def _loads(self, msg):
179 x = SOAPpy.parseSOAP(msg) 180 x = x.v1._asdict() 181 if x.has_key("auth"): 182 if x['auth']: x['auth'] = x['auth']._asdict() 183 if x.has_key("kwargs"): 184 try: 185 x['kwargs'] = x['kwargs']._asdict() 186 except: 187 x['kwargs'] = {} 188 x['args'] = x['args']._aslist() 189 return x
190 191 try: 192 import ZSI 193 from cStringIO import StringIO 194 _USE_ZSI = True 195 except: 196 _USE_ZSI = False 197 warn.debug("Unable to use SOAP to communicate (with ZSI module)") 198
199 -class SOAP_ZSI(SPyRO_Format):
200 - def __init__(self):
201 self.tcany = ZSI.TC.Any()
202
203 - def _dumps(self, msg):
204 o = StringIO() 205 w = ZSI.SoapWriter(o) 206 w.serialize(msg, self.tcany) 207 #w.serialize(msg) 208 w.close() 209 s = o.getvalue() 210 o.close() 211 return s
212
213 - def _loads(self, msg):
214 ps = ZSI.parse.ParsedSoap(msg) 215 return ps.Parse(self.tcany)
216 217 try: 218 import xmlrpclib 219 except: 220 warn.debug("Unable to use XMLRPC to communicate (with xmlrpclib module)") 221
222 -class XMLRPCFormat(SPyRO_Format):
223 """ XMLRPC Format, 224 225 SPyRO Clients can use the transparent mode. 226 Non-SPyRO clients must send request to any of the following functions 227 228 ==> SPyRO_execute(options) 229 Note: SPyRO_execute has an alias called 'execute' with the same arguments 230 Execute a dictionary (xmlrpc struct) with all the information to perform 231 a remote operation. The possible arguments depends of the type of the 232 request, (See the SPyRO's Request class). 233 The basic arguments or fields are: 234 |objname| The object name (ID in the server) 235 |attrname| The name of the requested attribute 236 |reqtype| The type of the request. It can be 'BYE', 'GET', 'SET', 'DEL', 237 CALL' 238 |auth| The authentication object to the request 239 240 Advanced arguments: 241 |local_server| If Request to call a remote method and exists port we call 242 the method with the pass the arguments by reference the server receives 243 the name or id in the remote peer (the caller) and a port to connect. 244 The IP address is obtained by the socket 245 |auth| The authentication object 246 |extra| Additional info to append to message, for example routed calls 247 248 When calling methods: 249 250 |args| Positional arguments, tuple 251 |kwargs| Keyword arguments, dictionary 252 253 254 Other specific functions are: 255 ==> getAttr(object_name, attribute_name) 256 Retrieves the value of the attribute |attribute_name| from the object 257 |object_name| 258 259 ==> setAttr(object_name, attribute_name, new_value) 260 Set the value to the attribute |attribute_name| of the object |object_name| 261 262 ==> callMethod(object_name, method_name, positional_args, keyword_args, options) 263 Call remote methods 264 |object_name| The name of the object 265 |method_name| The name of the method 266 |positional_args| The position arguments (an array or list) 267 |keyword_args| The named arguments (a struct or dictionary) 268 |options| Aditional arguments (see 'SPyRO_execute' 269 """
270 - def __init__(self, encoding="UTF-8"):
271 self.encoding = encoding
272
273 - def _loads(self, msg):
274 #warn.debug("LOAD:","**************" * 10, len(msg)) 275 #warn.debug(msg) 276 args, methodname = xmlrpclib.loads(msg) 277 #warn.debug("XXXXXXXX:",repr(args)) 278 return args[0]
279
280 - def _dumps(self, msg):
281 #warn.debug("DUMP:","==============" * 10) 282 #warn.debug(msg) 283 y = xmlrpclib.dumps((msg,) , methodname="SPyRO_execute", allow_none=1, encoding = self.encoding) 284 return y
285 286 ## server side dumps
287 - def load_request(self, msg):
288 x = { 289 'auth': None, 'local_server': None, 290 'rettype': spyro.SendByValue, 291 'reqtype' : spyro.Request.ReqCallMethod, 292 'args' : (), 'kwargs' : {}, 'extra': None 293 } 294 args, methodname = xmlrpclib.loads(msg) 295 options = {} 296 if methodname == 'SPyRO_execute' or methodname == 'execute': 297 options = args[0] 298 elif methodname == 'getAttr': 299 x['objname'], x['attrname'], options = args 300 x['reqtype'] = 'GET' 301 elif methodname == 'setAttr': 302 x['objname'], x['attrname'], x['value'], options = args 303 x['reqtype'] = 'SET' 304 elif methodname == 'callMethod': 305 x['objname'], x['attrname'], x['args'], x['kwargs'], options = args 306 x['reqtype'] = 'CALL' 307 x.update(options) 308 #warn.debug(x) 309 return x
310
311 - def dump_result(self, msg):
312 return xmlrpclib.dumps((msg,), methodresponse=1, 313 allow_none=1, encoding = self.encoding)
314
315 - def dump_error(self, msg):
316 return self.dump_result(msg)
317 318
319 -class XMLRPCFormatLegacy(XMLRPCFormat):
320 """ XMLRPC Format, 321 Non - SPyRO clients must send request to procedure "SPyRO_execute" with 322 the proper arguments (Request object properties) 323 """
324 - def __init__(self, encoding="UTF-8"):
325 self.encoding = encoding
326
327 - def _loads(self, msg):
328 #warn.debug("LOAD:","**************" * 10, len(msg)) 329 #warn.debug(msg) 330 args, methodname = xmlrpclib.loads(msg) 331 #warn.debug("XXXXXXXX:",repr(args)) 332 return args[0]
333
334 - def dump_control(self, msg):
335 # fake, its not supported 336 return xmlrpclib.dumps(('',''), methodname='SPyRO_control', 337 allow_none=1, encoding = self.encoding)
338
339 - def dump_callMethod(self, msg):
340 xmlrpcargs = tuple([msg['objname']] + list(msg['args'])) 341 y = xmlrpclib.dumps(xmlrpcargs, methodname=msg['attrname'], 342 allow_none=1, encoding = self.encoding) 343 return y
344 345 ## server side dumps
346 - def load_request(self, msg):
347 args, methodname = xmlrpclib.loads(msg) 348 x = { 349 'auth': None, 'local_server': None, 350 'rettype' : spyro.SendByValue, 351 'reqtype' : spyro.Request.ReqCallMethod, 352 'objname' : args[0], 353 'args' : args[1:], 354 'attrname' : methodname, 355 'kwargs' : {}, 'extra': None, 356 } 357 # warn.debug("REQUEST:", x) 358 return x
359
360 - def dump_result(self, msg):
361 return xmlrpclib.dumps((msg['retvalue'],), methodresponse=1, 362 allow_none=1, encoding = self.encoding)
363
364 - def dump_error(self, msg):
365 x = xmlrpclib.dumps(xmlrpclib.Fault(msg['errcode'],msg['retvalue']), 366 encoding = self.encoding) 367 return x
368
369 - def load_result(self, msg):
370 try: 371 args, methodname = xmlrpclib.loads(msg) 372 x = {'retvalue': args[0], 373 'rettype': spyro.SendByValue, 374 'errcode': spyro.Response.Successful} 375 except xmlrpclib.Fault, err: 376 x = {'retvalue': err.faultString, 377 'rettype': spyro.SendByValue, 378 'errcode': spyro.Response.ErrorExceptionThrowed} 379 return x
380
381 -def PrologChooseFormat(priorities, msg):
382 """ Choose the best format between local priorities and 383 the priorities in the msg 384 if |priorities| is None the Prolog.Priorities is used 385 """ 386 if not priorities: priorities = Prolog.Priorities 387 allentries = Prolog.EntryReader.findall(msg) 388 candidates = [] 389 for protocol, priority in allentries: 390 try: 391 candidates((priorities[protocol] * float(priority), protocol)) 392 except: 393 # the protocol does not exists or the priority is not 394 # a floating/int number 395 pass 396 candidates.sort() 397 format = None 398 try: 399 format = candidates[-1][0] 400 except: 401 warn.debug("There is not available format to perform connection: ", 402 priorities, allentries) 403 return format
404
405 -class Prolog(SPyRO_Format):
406 """ The prolog of the connection """ 407 EntryReader = re.compile(r"(\w+):(\d.\d)\n?") 408 Priorities = { 409 'PICKLE2':1.0, 410 'PICKLE1':0.9, 411 'PICKLE0':0.8, 412 'MARSHAL':0.7, 413 'XMLRPC':0.6, 414 'XMLRPC-Legacy':0.6, 415 'XML':0.5, 416 'WDDX':0.5, 417 'XMLGENERIC':0.5, 418 'SOAP':0.4, 419 'SOAPpy':0.3, 420 'SOAPZSI':0.2 421 } 422
423 - def __init__(self, priorities):
424 """ If |priorities| is None |Prolog.Priorities| is used instead""" 425 if priorities is None: 426 self.priorities = Prolog.Priorities 427 else: 428 self.priorities = priorities 429 self.isserver = True
430
431 - def join(self,x):
432 return "%s:%s"%x
433
434 - def _dumps(self, msg):
435 return "(Prolog)"
436
437 - def _loads(self, msg):
439 440 ## Server side
441 - def load_request(self, msg):
442 return Request(None, None, Request.ReqBye, None).__dict__
443
444 - def dump_result(self, msg):
445 return map("\n".join(map(self.join, self.priorities.items())))
446
447 - def dump_error(self, msg):
448 return self.dump_result(msg)
449 450
451 -class ORBFormats(DictType):
452 """ The Object Request Broker Format class """ 453
454 - def __init__(self):
455 DictType.__init__(self)
456
457 - def init(self):
458 self["MARSHAL"] = self.fmtMARSHAL 459 self["PICKLE"] = self.fmtPICKLE 460 self["PICKLE0"] = self.fmtPICKLE0 461 self["PICKLE1"] = self.fmtPICKLE1 462 self["PICKLE2"] = self.fmtPICKLE2 463 self["XML"] = self.fmtXML 464 self["WDDX"] = self.fmtWDDX 465 self["XMLGENERIC"] = self.fmtXMLGENERIC 466 self["XMLRPC"] = self.fmtXMLRPC 467 self["XMLRPC-Legacy"] = self.fmtXMLRPCLegacy 468 if _USE_SOAPpy: 469 self["SOAP"] = self.fmtSOAPpy 470 else: 471 self["SOAP"] = self.fmtZSI 472 self["SOAPpy"] = self.fmtSOAPpy 473 self["SOAPZSI"] = self.fmtZSI 474 self[""] = self.fmtDEFAULT 475 self["GUESS"] = self.fmtGUESS
476
477 - def setFormat(self, name, generator):
478 """ Register a new format, with name |name| and |interface| is function 479 that returns a fully functional interface to manage the format (i.e. a 480 new instance of the format manager) """ 481 self[name] = generator
482
483 - def fmtGUESS(self, *args, **kwargs):
484 return Prolog(*args, **kwargs)
485
486 - def fmtXMLRPC(self, *args, **kwargs):
487 return XMLRPCFormat()
488
489 - def fmtXMLRPCLegacy(self, *args, **kwargs):
490 return XMLRPCFormatLegacy()
491
492 - def fmtSOAPpy(self, *args, **kwargs):
493 return SOAP_SOAPpy()
494
495 - def fmtZSI(self, *args, **kwargs):
496 return SOAP_ZSI()
497
498 - def fmtMARSHAL(self, *args, **kwargs):
499 return MarshalWrapper(marshal)
500
501 - def fmtPICKLE(self, *args, **kwargs):
502 return PickleFormat(0)
503
504 - def fmtPICKLE0(self, *args, **kwargs):
505 return PickleFormat(0)
506
507 - def fmtPICKLE1(self, *args, **kwargs):
508 return PickleFormat(1)
509
510 - def fmtPICKLE2(self, *args, **kwargs):
511 return PickleFormat(2)
512
513 - def fmtXML(self, *args, **kwargs):
514 return MarshalWrapper(xml.marshal.generic)
515
516 - def fmtWDDX(self, *args, **kwargs):
517 return MarshalWrapper(xml.marshal.wddx)
518
519 - def fmtXMLGENERIC(self, *args, **kwargs):
520 return self.fmtXML(*args, **kwargs)
521
522 - def fmtDEFAULT(self, *args, **kwargs):
523 return self.fmtXMLRPC(*args, **kwargs)
524
525 - def getFormat(self, name, *args, **kwargs):
526 """ Retrieve a format broker |name| """ 527 return self[name](*args, **kwargs)
528