add process for target=new
[openerp-client.git] / bin / rpc.py
blobcd90b022555a81d6282188beb7dc336b69635449
1 ##############################################################################
3 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 # $Id$
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
12 # Service Company
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 ##############################################################################
30 import xmlrpclib
31 import logging
32 import socket
34 import tiny_socket
36 import service
37 import common
38 import options
39 import os
41 import re
43 class rpc_int_exception(Exception):
44 pass
47 class rpc_exception(Exception):
48 def __init__(self, code, backtrace):
50 self.code = code
51 self.args = backtrace
52 if hasattr(code, 'split'):
53 lines = code.split('\n')
55 self.type = lines[0].split(' -- ')[0]
56 self.message = ''
57 if len(lines[0].split(' -- ')) > 1:
58 self.message = lines[0].split(' -- ')[1]
60 self.data = '\n'.join(lines[2:])
61 else:
62 self.type = 'error'
63 self.message = backtrace
64 self.data = backtrace
66 self.backtrace = backtrace
68 log = logging.getLogger('rpc.exception')
69 log.warning('CODE %s: %s' % (str(code), self.message))
71 class gw_inter(object):
72 __slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
73 def __init__(self, url, db, uid, passwd, obj='/object'):
74 self._url = url
75 self._db = db
76 self._uid = uid
77 self._obj = obj
78 self._passwd = passwd
79 def exec_auth(method, *args):
80 pass
81 def execute(method, *args):
82 pass
84 class xmlrpc_gw(gw_inter):
85 __slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
86 def __init__(self, url, db, uid, passwd, obj='/object'):
87 gw_inter.__init__(self, url, db, uid, passwd, obj)
88 self._sock = xmlrpclib.ServerProxy(url+obj)
89 def exec_auth(self, method, *args):
90 logging.getLogger('rpc.request').info(str((method, self._db, self._uid, self._passwd, args)))
91 res = self.execute(method, self._uid, self._passwd, *args)
92 logging.getLogger('rpc.result').debug(str(res))
93 return res
95 def __convert(self, result):
96 if type(result)==type(u''):
97 return result.encode('utf-8')
98 elif type(result)==type([]):
99 return map(self.__convert, result)
100 elif type(result)==type({}):
101 newres = {}
102 for i in result.keys():
103 newres[i] = self.__convert(result[i])
104 return newres
105 else:
106 return result
108 def execute(self, method, *args):
109 result = getattr(self._sock,method)(self._db, *args)
110 return self.__convert(result)
112 class tinySocket_gw(gw_inter):
113 __slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
114 def __init__(self, url, db, uid, passwd, obj='/object'):
115 gw_inter.__init__(self, url, db, uid, passwd, obj)
116 self._sock = tiny_socket.mysocket()
117 self._obj = obj[1:]
118 def exec_auth(self, method, *args):
119 logging.getLogger('rpc.request').info(str((method, self._db, self._uid, self._passwd, args)))
120 res = self.execute(method, self._uid, self._passwd, *args)
121 logging.getLogger('rpc.result').debug(str(res))
122 return res
123 def execute(self, method, *args):
124 self._sock.connect(self._url)
125 self._sock.mysend((self._obj, method, self._db)+args)
126 res = self._sock.myreceive()
127 self._sock.disconnect()
128 return res
130 class rpc_session(object):
131 __slots__ = ('_open', '_url', 'uid', 'uname', '_passwd', '_gw', 'db', 'context', 'timezone')
132 def __init__(self):
133 self._open = False
134 self._url = None
135 self._passwd = None
136 self.uid = None
137 self.context = {}
138 self.uname = None
139 self._gw = xmlrpc_gw
140 self.db = None
141 self.timezone = 'utc'
143 def rpc_exec(self, obj, method, *args):
144 try:
145 sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
146 return sock.execute(method, *args)
147 except socket.error, e:
148 common.error(_('Connection refused !'), str(e), str(e))
149 raise rpc_exception(69, _('Connection refused!'))
150 except xmlrpclib.Fault, err:
151 raise rpc_exception(err.faultCode, err.faultString)
153 def rpc_exec_auth_try(self, obj, method, *args):
154 if self._open:
155 sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
156 return sock.exec_auth(method, *args)
157 else:
158 raise rpc_exception(1, 'not logged')
160 def rpc_exec_auth_wo(self, obj, method, *args):
161 try:
162 sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
163 return sock.exec_auth(method, *args)
164 except xmlrpclib.Fault, err:
165 a = rpc_exception(err.faultCode, err.faultString)
166 except tiny_socket.Myexception, err:
167 a = rpc_exception(err.faultCode, err.faultString)
168 if a.code in ('warning', 'UserError'):
169 common.warning(a.data, a.message)
170 return None
171 raise a
173 def rpc_exec_auth(self, obj, method, *args):
174 if self._open:
175 try:
176 sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
177 return sock.exec_auth(method, *args)
178 except socket.error, e:
179 common.error(_('Connection refused !'), str(e), str(e))
180 raise rpc_exception(69, 'Connection refused!')
181 except Exception, e:
182 if isinstance(e, xmlrpclib.Fault) \
183 or isinstance(e, tiny_socket.Myexception):
184 a = rpc_exception(e.faultCode, e.faultString)
185 if a.type in ('warning','UserError'):
186 if a.message in ('ConcurrencyException') and len(args) > 4:
187 if common.concurrency(args[0], args[2][0], args[4]):
188 if 'read_delta' in args[4]:
189 del args[4]['read_delta']
190 return self.rpc_exec_auth(obj, method, *args)
191 else:
192 common.warning(a.data, a.message)
193 else:
194 common.error(_('Application Error'), e.faultCode, e.faultString)
195 else:
196 common.error(_('Application Error'), _('View details'), str(e))
197 #TODO Must propagate the exception?
198 #raise
199 else:
200 raise rpc_exception(1, 'not logged')
202 def login(self, uname, passwd, url, port, protocol, db):
203 _protocol = protocol
204 if _protocol == 'http://' or _protocol == 'https://':
205 _url = _protocol + url+':'+str(port)+'/xmlrpc'
206 _sock = xmlrpclib.ServerProxy(_url+'/common')
207 self._gw = xmlrpc_gw
208 try:
209 res = _sock.login(db or '', uname or '', passwd or '')
210 except socket.error,e:
211 return -1
212 if not res:
213 self._open=False
214 self.uid=False
215 return -2
216 else:
217 _url = _protocol+url+':'+str(port)
218 _sock = tiny_socket.mysocket()
219 self._gw = tinySocket_gw
220 try:
221 _sock.connect(url, int(port))
222 _sock.mysend(('common', 'login', db or '', uname or '', passwd or ''))
223 res = _sock.myreceive()
224 _sock.disconnect()
225 except socket.error,e:
226 return -1
227 if not res:
228 self._open=False
229 self.uid=False
230 return -2
231 self._url = _url
232 self._open = True
233 self.uid = res
234 self.uname = uname
235 self._passwd = passwd
236 self.db = db
238 #CHECKME: is this useful? maybe it's used to see if there is no
239 # exception raised?
240 sock = self._gw(self._url, self.db, self.uid, self._passwd)
241 self.context_reload()
242 return 1
244 def list_db(self, url):
245 m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '')
246 if not m:
247 return -1
248 if m.group(1) == 'http://' or m.group(1) == 'https://':
249 sock = xmlrpclib.ServerProxy(url + '/xmlrpc/db')
250 try:
251 return sock.list()
252 except:
253 return -1
254 else:
255 sock = tiny_socket.mysocket()
256 try:
257 sock.connect(m.group(2), int(m.group(3)))
258 sock.mysend(('db', 'list'))
259 res = sock.myreceive()
260 sock.disconnect()
261 return res
262 except Exception, e:
263 return -1
265 def db_exec_no_except(self, url, method, *args):
266 m = re.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url or '')
267 if m.group(1) == 'http://' or m.group(1) == 'https://':
268 sock = xmlrpclib.ServerProxy(url + '/xmlrpc/db')
269 return getattr(sock, method)(*args)
270 else:
271 sock = tiny_socket.mysocket()
272 sock.connect(m.group(2), int(m.group(3)))
273 sock.mysend(('db', method)+args)
274 res = sock.myreceive()
275 sock.disconnect()
276 return res
278 def db_exec(self, url, method, *args):
279 res = False
280 try:
281 res = self.db_exec_no_except(url, method, *args)
282 except socket.error, msg:
283 common.warning('Could not contact server!')
284 return res
286 def context_reload(self):
287 self.context = {}
288 self.timezone = 'utc'
289 # self.uid
290 self.context = self.rpc_exec_auth('/object', 'execute', 'res.users', 'context_get')
291 if 'lang' in self.context:
292 import translate
293 translate.setlang(self.context['lang'])
294 options.options['client.lang']=self.context['lang']
295 ids = self.rpc_exec_auth('/object', 'execute', 'res.lang', 'search', [('code', '=', self.context['lang'])])
296 if ids:
297 l = self.rpc_exec_auth('/object', 'execute', 'res.lang', 'read', ids, ['direction'])
298 if l and 'direction' in l[0]:
299 common.DIRECTION = l[0]['direction']
300 import gtk
301 if common.DIRECTION == 'rtl':
302 gtk.widget_set_default_direction(gtk.TEXT_DIR_RTL)
303 else:
304 gtk.widget_set_default_direction(gtk.TEXT_DIR_LTR)
305 if 'tz' in self.context:
306 self.timezone = self.rpc_exec_auth('/common', 'timezone_get')
307 try:
308 import pytz
309 except:
310 common.warning('You select a timezone but tinyERP could not find pytz library !\nThe timezone functionality will be disable.')
312 def logged(self):
313 return self._open
315 def logout(self):
316 if self._open:
317 self._open = False
318 self.uname = None
319 self.uid = None
320 self._passwd = None
321 else:
322 pass
324 session = rpc_session()
327 class RPCProxy(object):
329 def __init__(self, resource):
330 self.resource = resource
331 self.__attrs = {}
333 def __getattr__(self, name):
334 if not name in self.__attrs:
335 self.__attrs[name] = RPCFunction(self.resource, name)
336 return self.__attrs[name]
339 class RPCFunction(object):
341 def __init__(self, object, func_name):
342 self.object = object
343 self.func = func_name
345 def __call__(self, *args):
346 return session.rpc_exec_auth('/object', 'execute', self.object, self.func, *args)