[REF] code review

bzr revid: qdp-launchpad@openerp.com-20130215143503-6a6x53gdeb6j33tm
This commit is contained in:
Quentin (OpenERP) 2013-02-15 15:35:03 +01:00
parent 74ba45d1fc
commit 0a27066e54
5 changed files with 30 additions and 286 deletions

View File

@ -594,16 +594,13 @@ class res_config_settings(osv.osv_memory):
def get_option_path(self, cr, uid, menu_xml_id, context=None): def get_option_path(self, cr, uid, menu_xml_id, context=None):
""" """
Fetch the path to a specified configuration view and the action id Fetch the path to a specified configuration view and the action id to access it.
to access it.
:param string menu_xml_id: the xml id of the menuitem where the view :param string menu_xml_id: the xml id of the menuitem where the view is located,
is located, structured as follows: module_name.menuitem_xml_id structured as follows: module_name.menuitem_xml_id (e.g.: "base.menu_sale_config")
(e.g.: "base.menu_sale_config")
:return tuple: :return tuple:
- t[0]: string: full path to the menuitem - t[0]: string: full path to the menuitem (e.g.: "Settings/Configuration/Sales")
(e.g.: "Settings/Configuration/Sales") - t[1]: long: id of the menuitem's action
- t[1]: long: id of the menuitem's action
""" """
module_name, menu_xml_id = menu_xml_id.split('.') module_name, menu_xml_id = menu_xml_id.split('.')
dummy, menu_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, module_name, menu_xml_id) dummy, menu_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, module_name, menu_xml_id)
@ -615,11 +612,9 @@ class res_config_settings(osv.osv_memory):
""" """
Fetch the human readable name of a specified configuration option. Fetch the human readable name of a specified configuration option.
:param string full_field_name: the full name of the field, structured :param string full_field_name: the full name of the field, structured as follows:
as follows: model_name.field_name model_name.field_name (e.g.: "sale.config.settings.fetchmail_lead")
(e.g.: "sale.config.settings.fetchmail_lead") :return string: human readable name of the field (e.g.: "Create leads from incoming mails")
:return string: human readable name of the field
(e.g.: "Create leads from incoming mails")
""" """
model_name, field_name = full_field_name.rsplit('.', 1) model_name, field_name = full_field_name.rsplit('.', 1)
@ -627,22 +622,23 @@ class res_config_settings(osv.osv_memory):
def get_config_warning(self, cr, msg, context=None): def get_config_warning(self, cr, msg, context=None):
""" """
Helper: return a Warning exception with the given message where the Helper: return a Warning exception with the given message where the %(field:xxx)s
%(field:)s and/or %(menu:)s are replaced by the human readable field's name and/or %(menu:yyy)s are replaced by the human readable field's name and/or menuitem's
and/or menuitem's full path. full path.
Usage: Usage:
------ ------
Just include in your error message %(field:model_name.field_name)s to Just include in your error message %(field:model_name.field_name)s to obtain the human
obtain the human readable field's name, and/or readable field's name, and/or %(menu:module_name.menuitem_xml_id)s to obtain the menuitem's
%(menu:module_name.menuitem_xml_id)s to obtain the menuitem's full path. full path.
Example of use: Example of use:
--------------- ---------------
from openerp.addons.base.res.res_config import get_warning_config from openerp.addons.base.res.res_config import get_warning_config
raise get_warning_config(cr, _("Error: this action is prohibited. You should check the field %(field:sale.config.settings.fetchmail_lead)s in %(menu:base.menu_sale_config)s."), context=context) raise get_warning_config(cr, _("Error: this action is prohibited. You should check the field %(field:sale.config.settings.fetchmail_lead)s in %(menu:base.menu_sale_config)s."), context=context)
will return an exception containing the following message:
Error: this action is prohibited. You should check the field Create leads from incoming mails in Settings/Configuration/Sales. This will return an exception containing the following message:
Error: this action is prohibited. You should check the field Create leads from incoming mails in Settings/Configuration/Sales.
""" """
res_config_obj = pooler.get_pool(cr.dbname).get('res.config.settings') res_config_obj = pooler.get_pool(cr.dbname).get('res.config.settings')
@ -656,18 +652,15 @@ class res_config_settings(osv.osv_memory):
# human readable field's name) and the action_id if any # human readable field's name) and the action_id if any
values = {} values = {}
action_id = None action_id = None
buttontext = None
for item in references: for item in references:
ref_type, ref = item.split(':') ref_type, ref = item.split(':')
if ref_type == 'menu': if ref_type == 'menu':
values[item], action_id = res_config_obj.get_option_path(cr, SUPERUSER_ID, ref, context) values[item], action_id = res_config_obj.get_option_path(cr, SUPERUSER_ID, ref, context=context)
buttontext = _('Go to the configuration panel')
elif ref_type == 'field': elif ref_type == 'field':
values[item] = res_config_obj.get_option_name(cr, SUPERUSER_ID, ref, context) values[item] = res_config_obj.get_option_name(cr, SUPERUSER_ID, ref, context=context)
# 3/ substitute and return the result # 3/ substitute and return the result
if (action_id): if (action_id):
return exceptions.RedirectWarning(msg % values, action_id, _('Go to the configuration panel')) return exceptions.RedirectWarning(msg % values, action_id, _('Go to the configuration panel'))
else: return exceptions.Warning(msg % values)
return exceptions.Warning(msg % values)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -25,8 +25,7 @@ This module defines a few exception types. Those types are understood by the
RPC layer. Any other exception type bubbling until the RPC layer will be RPC layer. Any other exception type bubbling until the RPC layer will be
treated as a 'Server error'. treated as a 'Server error'.
If you consider introducing new exceptions, check out the test_exceptions If you consider introducing new exceptions, check out the test_exceptions addon.
addon.
""" """
class Warning(Exception): class Warning(Exception):
@ -34,16 +33,13 @@ class Warning(Exception):
class RedirectWarning(Exception): class RedirectWarning(Exception):
""" Warning with a possibility to redirect the user instead of simply """ Warning with a possibility to redirect the user instead of simply
discarding the warning message. diplaying the warning message.
Should receive as parameters:
:param int action_id: id of the action where to perform the redirection
:param string button_text: text to put on the button that will trigger
the redirection.
""" """
def __init__(self, msg, action_id, button_text):
"""
:param int action_id: id of the action required to perform the
redirection
:param string button_text: text to put on the button which will trigger
the redirection
"""
super(RedirectWarning, self).__init__(msg, action_id, button_text)
class AccessDenied(Exception): class AccessDenied(Exception):
""" Login/password error. No message, no traceback. """ """ Login/password error. No message, no traceback. """

View File

@ -3500,6 +3500,7 @@ class BaseModel(object):
res = {} res = {}
translation_obj = self.pool.get('ir.translation')
for parent in self._inherits: for parent in self._inherits:
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context)) res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
@ -3515,8 +3516,6 @@ class BaseModel(object):
res[f]['states'] = {} res[f]['states'] = {}
if 'lang' in context: if 'lang' in context:
translation_obj = self.pool.get('ir.translation')
if 'string' in res[f]: if 'string' in res[f]:
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context['lang']) res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context['lang'])
if res_trans: if res_trans:

View File

@ -1,245 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
#.apidoc title: NET-RPC Server
""" This file contains instance of the net-rpc server
"""
import logging
import select
import socket
import sys
import threading
import traceback
import openerp
import openerp.service.netrpc_socket
import openerp.netsvc as netsvc
import openerp.tools as tools
_logger = logging.getLogger(__name__)
class Server:
""" Generic interface for all servers with an event loop etc.
Override this to impement http, net-rpc etc. servers.
Servers here must have threaded behaviour. start() must not block,
there is no run().
"""
__is_started = False
__servers = []
__starter_threads = []
# we don't want blocking server calls (think select()) to
# wait forever and possibly prevent exiting the process,
# but instead we want a form of polling/busy_wait pattern, where
# _server_timeout should be used as the default timeout for
# all I/O blocking operations
_busywait_timeout = 0.5
def __init__(self):
Server.__servers.append(self)
if Server.__is_started:
# raise Exception('All instances of servers must be inited before the startAll()')
# Since the startAll() won't be called again, allow this server to
# init and then start it after 1sec (hopefully). Register that
# timer thread in a list, so that we can abort the start if quitAll
# is called in the meantime
t = threading.Timer(1.0, self._late_start)
t.name = 'Late start timer for %s' % str(self.__class__)
Server.__starter_threads.append(t)
t.start()
def start(self):
_logger.debug("called stub Server.start")
def _late_start(self):
self.start()
for thr in Server.__starter_threads:
if thr.finished.is_set():
Server.__starter_threads.remove(thr)
def stop(self):
_logger.debug("called stub Server.stop")
def stats(self):
""" This function should return statistics about the server """
return "%s: No statistics" % str(self.__class__)
@classmethod
def startAll(cls):
if cls.__is_started:
return
_logger.info("Starting %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.start()
cls.__is_started = True
@classmethod
def quitAll(cls):
if not cls.__is_started:
return
_logger.info("Stopping %d services" % len(cls.__servers))
for thr in cls.__starter_threads:
if not thr.finished.is_set():
thr.cancel()
cls.__starter_threads.remove(thr)
for srv in cls.__servers:
srv.stop()
cls.__is_started = False
@classmethod
def allStats(cls):
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
res.extend(srv.stats() for srv in cls.__servers)
return '\n'.join(res)
def _close_socket(self):
netsvc.close_socket(self.socket)
class TinySocketClientThread(threading.Thread):
def __init__(self, sock, threads):
spn = sock and sock.getpeername()
spn = 'netrpc-client-%s:%s' % spn[0:2]
threading.Thread.__init__(self, name=spn)
self.sock = sock
# Only at the server side, use a big timeout: close the
# clients connection when they're idle for 20min.
self.sock.settimeout(1200)
self.threads = threads
def run(self):
self.running = True
try:
ts = openerp.server.netrpc_socket.mysocket(self.sock)
except Exception:
self.threads.remove(self)
self.running = False
return False
while self.running:
try:
msg = ts.myreceive()
result = netsvc.dispatch_rpc(msg[0], msg[1], msg[2:])
ts.mysend(result)
except socket.timeout:
#terminate this channel because other endpoint is gone
break
except Exception, e:
try:
valid_exception = Exception(netrpc_handle_exception_legacy(e))
valid_traceback = getattr(e, 'traceback', sys.exc_info())
formatted_traceback = "".join(traceback.format_exception(*valid_traceback))
_logger.debug("netrpc: communication-level exception", exc_info=True)
ts.mysend(valid_exception, exception=True, traceback=formatted_traceback)
break
except Exception, ex:
#terminate this channel if we can't properly send back the error
_logger.exception("netrpc: cannot deliver exception message to client")
break
netsvc.close_socket(self.sock)
self.sock = None
self.threads.remove(self)
self.running = False
return True
def stop(self):
self.running = False
def netrpc_handle_exception_legacy(e):
if isinstance(e, openerp.osv.osv.except_osv):
return 'warning -- ' + e.name + '\n\n' + e.value
if isinstance(e, openerp.exceptions.Warning):
return 'warning -- Warning\n\n' + str(e)
if isinstance(e, openerp.exceptions.AccessError):
return 'warning -- AccessError\n\n' + str(e)
if isinstance(e, openerp.exceptions.AccessDenied):
return 'AccessDenied ' + str(e)
return openerp.tools.exception_to_unicode(e)
class TinySocketServerThread(threading.Thread,Server):
def __init__(self, interface, port, secure=False):
threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port)
Server.__init__(self)
self.__port = port
self.__interface = interface
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.__interface, self.__port))
self.socket.listen(5)
self.threads = []
_logger.info("starting NET-RPC service on %s:%s", interface or '0.0.0.0', port)
def run(self):
try:
self.running = True
while self.running:
fd_sets = select.select([self.socket], [], [], self._busywait_timeout)
if not fd_sets[0]:
continue
(clientsocket, address) = self.socket.accept()
ct = TinySocketClientThread(clientsocket, self.threads)
clientsocket = None
self.threads.append(ct)
ct.start()
lt = len(self.threads)
if (lt > 10) and (lt % 10 == 0):
# Not many threads should be serving at the same time, so log
# their abuse.
_logger.debug("Netrpc: %d threads", len(self.threads))
self.socket.close()
except Exception, e:
_logger.warning("Netrpc: closing because of exception %s", e)
self.socket.close()
return False
def stop(self):
self.running = False
for t in self.threads:
t.stop()
self._close_socket()
def stats(self):
res = "Net-RPC: " + ( (self.running and "running") or "stopped")
i = 0
for t in self.threads:
i += 1
res += "\nNet-RPC #%d: %s " % (i, t.name)
if t.isAlive():
res += "running"
else:
res += "finished"
if t.sock:
res += ", socket"
return res
netrpcd = None
def start_service():
global netrpcd
if tools.config.get('netrpc', False):
netrpcd = TinySocketServerThread(tools.config.get('netrpc_interface', ''), int(tools.config.get('netrpc_port', 8070)))
def stop_service():
Server.quitAll()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -94,8 +94,9 @@ def xmlrpc_handle_exception(e):
if isinstance(e, openerp.osv.orm.except_orm): # legacy if isinstance(e, openerp.osv.orm.except_orm): # legacy
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value)) fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, openerp.tools.ustr(e.value))
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
elif isinstance(e, openerp.exceptions.Warning): elif isinstance(e, openerp.exceptions.Warning) or isinstance(e, openerp.exceptions.RedirectWarning):
fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e)) fault = xmlrpclib.Fault(RPC_FAULT_CODE_WARNING, str(e))
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)
elif isinstance (e, openerp.exceptions.AccessError): elif isinstance (e, openerp.exceptions.AccessError):
fault = xmlrpclib.Fault(RPC_FAULT_CODE_ACCESS_ERROR, str(e)) fault = xmlrpclib.Fault(RPC_FAULT_CODE_ACCESS_ERROR, str(e))
response = xmlrpclib.dumps(fault, allow_none=False, encoding=None) response = xmlrpclib.dumps(fault, allow_none=False, encoding=None)