replaced str() calls with unicode()
[OpenSRF.git] / src / python / osrf / gateway.py
1 from xml.dom import minidom
2 from xml.sax import handler, make_parser, saxutils
3 from osrf.json import *
4 from osrf.net_obj import *
5 from osrf.log import *
6 import urllib, urllib2, sys, re
7
8 defaultHost = None
9
10 class GatewayRequest:
11     def __init__(self, service, method, params=[]):
12         self.service = service
13         self.method = method
14         self.params = params
15         self.path = 'gateway'
16
17     def setPath(self, path):
18         self.path = path
19
20     def send(self):
21         params = self.buildPOSTParams()
22         request = urllib2.Request(self.buildURL(), data=params)
23         response = None
24         try:
25             response =urllib2.urlopen(request)
26         except urllib2.HTTPError, e:
27             # log this?
28             sys.stderr.write('%s => %s?%s\n' % (unicode(e), self.buildURL(), params))
29             raise e
30             
31         return self.handleResponse(response)
32
33     def buildPOSTParams(self):
34
35         params = urllib.urlencode({   
36             'service': self.service,
37             'method': self.method,
38             'format': self.getFormat(),
39             'input_format': self.getInputFormat()
40         })
41
42         for p in self.params:
43             params += '&param=%s' % urllib.quote(self.encodeParam(p), "'/")
44         return params
45
46     def setDefaultHost(host):
47         global defaultHost
48         defaultHost = host
49     setDefaultHost = staticmethod(setDefaultHost)
50
51     def buildURL(self):
52         return 'http://%s/%s' % (defaultHost, self.path)
53
54 class JSONGatewayRequest(GatewayRequest):
55     def __init__(self, service, method, *params):
56         GatewayRequest.__init__(self, service, method, list(params))
57
58     def getFormat(self):
59         return 'json'
60
61     def getInputFormat(self):
62         return self.getFormat()
63
64     def handleResponse(self, response):
65         s = response.read()
66         obj = osrfJSONToObject(s)
67         if obj['status'] != 200:
68             sys.stderr.write('JSON gateway returned status %d:\n%s\n' % (obj['status'], s))
69             return None
70
71         # the gateway wraps responses in an array to handle streaming data
72         # if there is only one item in the array, it (probably) wasn't a streaming request
73         p = obj['payload']
74         if len(p) > 1: return p
75         return p[0]
76
77     def encodeParam(self, param):
78         return osrfObjectToJSON(param)
79
80 class XMLGatewayRequest(GatewayRequest):
81
82     def __init__(self, service, method, *params):
83         GatewayRequest.__init__(self, service, method, list(params))
84
85     def getFormat(self):
86         return 'xml'
87
88     def getInputFormat(self):
89         return self.getFormat()
90
91     def handleResponse(self, response):
92         handler = XMLGatewayParser()
93         parser = make_parser()
94         parser.setContentHandler(handler)
95         try:
96             parser.parse(response)
97         except Exception, e:
98             osrfLogErr('Error parsing gateway XML: %s' % unicode(e))
99             return None
100
101         return handler.getResult()
102
103     def encodeParam(self, param):
104         return osrfObjectToXML(param);
105
106 class XMLGatewayParser(handler.ContentHandler):
107
108     def __init__(self):
109         self.result = None
110         self.objStack = []
111         self.keyStack = []
112         self.posStack = [] # for tracking array-based hinted object indices
113
114         # true if we are parsing an element that may have character data
115         self.charsPending = 0 
116
117     def getResult(self):
118         return self.result
119
120     def __getAttr(self, attrs, name):
121         for (k, v) in attrs.items():
122             if k == name:
123                 return v
124         return None
125
126     def startElement(self, name, attrs):
127         
128         if self.charsPending:
129             # we just read a 'string' or 'number' element that resulted
130             # in no text data.  Appaned a None object
131             self.appendChild(None)
132
133         if name == 'null':
134             self.appendChild(None)
135             return
136
137         if name == 'string' or name == 'number':
138             self.charsPending = True
139             return
140
141         if name == 'element': # this is an object item wrapper
142             self.keyStack.append(self.__getAttr(attrs, 'key'))
143             return
144
145         hint = self.__getAttr(attrs, 'class_hint')
146         if hint:
147             obj = osrfNewObjectFromHint(hint)
148             self.appendChild(obj)
149             self.objStack.append(obj)
150             if name == 'array':
151                 self.posStack.append(0)
152             return
153
154         if name == 'array':
155             obj = []
156             self.appendChild(obj)
157             self.objStack.append(obj)
158             return
159
160         if name == 'object':
161             obj = {}
162             self.appendChild(obj)
163             self.objStack.append(obj)
164             return
165
166         if name == 'boolean':
167             self.appendChild((self.__getAttr(attrs, 'value') == 'true'))
168             return
169
170
171     def appendChild(self, child):
172
173         if self.result == None:
174             self.result = child
175
176         if not self.objStack: return;
177
178         parent = self.objStack[len(self.objStack)-1]
179
180         if isinstance(parent, list):
181             parent.append(child)
182         else:
183             if isinstance(parent, dict):
184                 parent[self.keyStack.pop()] = child
185             else:
186                 if isinstance(parent, osrfNetworkObject):
187                     key = None
188                     if parent.getRegistry().wireProtocol == 'array':
189                         keys = parent.getRegistry().keys
190                         i = self.posStack.pop()
191                         key = keys[i]
192                         if i+1 < len(keys):
193                             self.posStack.append(i+1)
194                     else:
195                         key = self.keyStack.pop()
196
197                     parent.setField(key, child)
198
199     def endElement(self, name):
200         if name == 'array' or name == 'object':
201             self.objStack.pop()
202
203     def characters(self, chars):
204         self.charsPending = False
205         self.appendChild(urllib.unquote_plus(chars))
206
207
208
209     
210