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