1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
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
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 ##############################################################################
44 class rpc_int_exception(Exception):
48 class rpc_exception(Exception):
49 def __init__(self
, code
, backtrace
):
53 if hasattr(code
, 'split'):
54 lines
= code
.split('\n')
56 self
.type = lines
[0].split(' -- ')[0]
58 if len(lines
[0].split(' -- ')) > 1:
59 self
.message
= lines
[0].split(' -- ')[1]
61 self
.data
= '\n'.join(lines
[2:])
64 self
.message
= 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'):
80 def exec_auth(method
, *args
):
82 def execute(method
, *args
):
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
))
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({}):
103 for i
in result
.keys():
104 newres
[i
] = self
.__convert
(result
[i
])
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()
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
))
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()
131 class rpc_session(object):
132 __slots__
= ('_open', '_url', 'uid', 'uname', '_passwd', '_gw', 'db', 'context', 'timezone')
142 self
.timezone
= 'utc'
144 def rpc_exec(self
, obj
, method
, *args
):
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
):
156 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
, obj
)
157 return sock
.exec_auth(method
, *args
)
159 raise rpc_exception(1, 'not logged')
161 def rpc_exec_auth_wo(self
, obj
, method
, *args
):
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
)
174 def rpc_exec_auth(self
, obj
, method
, *args
):
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!')
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
)
193 common
.warning(a
.data
, a
.message
)
195 common
.error(_('Application Error'), e
.faultCode
, e
.faultString
)
197 common
.error(_('Application Error'), _('View details'), str(e
))
198 #TODO Must propagate the exception?
201 raise rpc_exception(1, 'not logged')
203 def login(self
, uname
, passwd
, url
, port
, protocol
, db
):
205 if _protocol
== 'http://' or _protocol
== 'https://':
206 _url
= _protocol
+ url
+':'+str(port
)+'/xmlrpc'
207 _sock
= xmlrpclib
.ServerProxy(_url
+'/common')
210 res
= _sock
.login(db
or '', uname
or '', passwd
or '')
211 except socket
.error
,e
:
218 _url
= _protocol
+url
+':'+str(port
)
219 _sock
= tiny_socket
.mysocket()
220 self
._gw
= tinySocket_gw
222 _sock
.connect(url
, int(port
))
223 _sock
.mysend(('common', 'login', db
or '', uname
or '', passwd
or ''))
224 res
= _sock
.myreceive()
226 except socket
.error
,e
:
236 self
._passwd
= passwd
239 #CHECKME: is this useful? maybe it's used to see if there is no
241 sock
= self
._gw
(self
._url
, self
.db
, self
.uid
, self
._passwd
)
242 self
.context_reload()
245 def list_db(self
, url
):
246 m
= re
.match('^(http[s]?://|socket://)([\w.\-]+):(\d{1,5})$', url
or '')
249 if m
.group(1) == 'http://' or m
.group(1) == 'https://':
250 sock
= xmlrpclib
.ServerProxy(url
+ '/xmlrpc/db')
256 sock
= tiny_socket
.mysocket()
258 sock
.connect(m
.group(2), int(m
.group(3)))
259 sock
.mysend(('db', 'list'))
260 res
= sock
.myreceive()
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
)
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()
279 def db_exec(self
, url
, method
, *args
):
282 res
= self
.db_exec_no_except(url
, method
, *args
)
283 except socket
.error
, msg
:
284 common
.warning('Could not contact server!')
287 def context_reload(self
):
289 self
.timezone
= 'utc'
291 self
.context
= self
.rpc_exec_auth('/object', 'execute', 'res.users', 'context_get')
292 if 'lang' in self
.context
:
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'])])
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']
302 if common
.DIRECTION
== 'rtl':
303 gtk
.widget_set_default_direction(gtk
.TEXT_DIR_RTL
)
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')
311 common
.warning('You select a timezone but OpenERP could not find pytz library !\nThe timezone functionality will be disable.')
325 session
= rpc_session()
328 class RPCProxy(object):
330 def __init__(self
, resource
):
331 self
.resource
= resource
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
):
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: