import copy
import sys
import traceback
-
-from psycopg2 import IntegrityError
+import logging
+from psycopg2 import IntegrityError, errorcodes
from tools.func import wraps
-
+from tools.translate import translate
module_list = []
module_class_list = {}
def check(f):
@wraps(f)
def wrapper(self, dbname, *args, **kwargs):
+ """ Wraps around OSV functions and normalises a few exceptions
+ """
+
+ def tr(src, ttype):
+ # We try to do the same as the _(), but without the frame
+ # inspection, since we aready are wrapping an osv function
+ # trans_obj = self.get('ir.translation') cannot work yet :(
+ ctx = {}
+ if not kwargs:
+ if args and isinstance(args[-1], dict):
+ ctx = args[-1]
+ else:
+ ctx = {}
+ elif isinstance(kwargs, dict):
+ ctx = kwargs.get('context', {})
+ else:
+ ctx = {}
+
+ uid = 1
+ if args and isinstance(args[0], (long, int)):
+ uid = args[0]
+
+ lang = ctx and ctx.get('lang', False)
+ if not (lang or hasattr(src, '__call__')):
+ return src
+
+ # We open a *new* cursor here, one reason is that failed SQL
+ # queries (as in IntegrityError) will invalidate the current one.
+ cr = False
+
+ if hasattr(src, '__call__'):
+ # callable. We need to find the right parameters to call
+ # the orm._sql_message(self, cr, uid, ids, context) function,
+ # or we skip..
+ # our signature is f(osv_pool, dbname [,uid, obj, method, args])
+ try:
+ if args and len(args) > 1:
+ obj = self.get(args[1])
+ if len(args) > 3 and isinstance(args[3], (long, int, list)):
+ ids = args[3]
+ else:
+ ids = []
+ cr = pooler.get_db_only(dbname).cursor()
+ return src(obj, cr, uid, ids, context=(ctx or {}))
+ except Exception, e:
+ pass
+ finally:
+ if cr: cr.close()
+
+ return False # so that the original SQL error will
+ # be returned, it is the best we have.
+
+ try:
+ cr = pooler.get_db_only(dbname).cursor()
+ #return trans_obj._get_source( name=?)
+ res = translate(cr, name=False, source_type=ttype,
+ lang=lang, source=src)
+ if res:
+ return res
+ else:
+ return src
+ finally:
+ if cr: cr.close()
+
+ def _(src):
+ return tr(src, 'code')
+
try:
if not pooler.get_pool(dbname)._ready:
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
return f(self, dbname, *args, **kwargs)
except orm.except_orm, inst:
if inst.name == 'AccessError':
- tb_s = "AccessError\n" + "".join(traceback.format_exception(*sys.exc_info()))
- self.logger.notifyChannel('web-services', netsvc.LOG_DEBUG, tb_s)
+ self.logger.debug("AccessError", exc_info=True)
self.abortResponse(1, inst.name, 'warning', inst.value)
except except_osv, inst:
self.abortResponse(1, inst.name, inst.exc_type, inst.value)
except IntegrityError, inst:
for key in self._sql_error.keys():
if key in inst[0]:
- self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
- self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
+ self.abortResponse(1, _('Constraint Error'), 'warning',
+ tr(self._sql_error[key], 'sql_constraint') or inst[0])
+ if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
+ msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
+ self.logger.debug("IntegrityError", exc_info=True)
+ try:
+ errortxt = inst.pgerror.replace('«','"').replace('»','"')
+ if '"public".' in errortxt:
+ context = errortxt.split('"public".')[1]
+ model_name = table = context.split('"')[1]
+ else:
+ last_quote_end = errortxt.rfind('"')
+ last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
+ model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
+ print "MODEL", last_quote_begin, last_quote_end, model_name
+ model = table.replace("_",".")
+ model_obj = self.get(model)
+ if model_obj:
+ model_name = model_obj._description or model_obj._name
+ msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
+ except Exception:
+ pass
+ self.abortResponse(1, _('Integrity Error'), 'warning', msg)
+ else:
+ self.abortResponse(1, _('Integrity Error'), 'warning', inst[0])
except Exception, e:
- tb_s = "".join(traceback.format_exception(*sys.exc_info()))
- self.logger.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s)
+ self.logger.exception("Uncaught exception")
raise
return wrapper
self._store_function = {}
self._init = True
self._init_parent = {}
- self.logger = netsvc.Logger()
+ self.logger = logging.getLogger("web-services")
netsvc.Service.__init__(self, 'object_proxy', audience='')
self.exportMethod(self.obj_list)
self.exportMethod(self.exec_workflow)
try:
try:
if method.startswith('_'):
- raise except_osv('Method Error', 'Private method %s can not be calleble.' % (method,))
+ raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
if res is None:
- self.logger.notifyChannel("web-services", netsvc.LOG_WARNING,
- 'Method can not return a None value (crash in XML-RPC)')
+ self.logger.warning('The method %s of the object %s can not return `None` !', method, obj)
cr.commit()
except Exception:
cr.rollback()
for c in cls.__dict__.get(s, []):
exist = False
for c2 in range(len(new)):
- if new[c2][2]==c[2]:
+ #For _constraints, we should check field and methods as well
+ if new[c2][2]==c[2] and new[c2][0]==c[0]:
new[c2] = c
exist = True
break