diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 74da6bdfd5d..46c81d5a041 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -191,8 +191,8 @@ class ir_ui_menu(osv.osv): if context is None: context = {} ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] + if self.CONCURRENCY_CHECK_FIELD in ctx: + del ctx[self.CONCURRENCY_CHECK_FIELD] values_obj = self.pool.get('ir.values') values_ids = values_obj.search(cursor, user, [ ('model', '=', self._name), ('key', '=', 'action'), diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 116a581c3b0..087b14dc99d 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -43,8 +43,8 @@ class ir_values(osv.osv): if context is None: context = {} ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] + if self.CONCURRENCY_CHECK_FIELD in ctx: + del ctx[self.CONCURRENCY_CHECK_FIELD] if not self.browse(cursor, user, id, context=context).object: value = pickle.dumps(eval(value)) self.write(cursor, user, id, {name[:-9]: value}, context=ctx) diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index 35c1e9f48ca..a4f822a5e16 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -462,9 +462,12 @@ class module(osv.osv): for mod in self.browse(cr, uid, ids): if mod.state != 'installed': continue - + modpath = addons.get_module_path(mod.name) + if not modpath: + # unable to find the module. we skip + continue for lang in filter_lang: - f = os.path.join(addons.get_module_path(mod.name), 'i18n', lang + '.po') + f = os.path.join(modpath, 'i18n', lang + '.po') if os.path.exists(f): logger.notifyChannel("init", netsvc.LOG_INFO, 'module %s: loading translation file for language %s' % (mod.name, lang)) tools.trans_load(cr.dbname, f, lang, verbose=False) diff --git a/bin/netsvc.py b/bin/netsvc.py index b0e5035e29f..745921023e3 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -228,7 +228,10 @@ class OpenERPDispatcher: return result except Exception, e: self.log('exception', tools.exception_to_unicode(e)) - tb = sys.exc_info() + if hasattr(e, 'traceback'): + tb = e.traceback + else: + tb = sys.exc_info() tb_s = "".join(traceback.format_exception(*tb)) if tools.config['debug_mode']: import pdb diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 3dcc510db27..f6a54423894 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -303,6 +303,8 @@ class orm_template(object): _inherits = {} _table = None _invalids = set() + + CONCURRENCY_CHECK_FIELD = '__last_update' def _field_create(self, cr, context={}): cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,)) @@ -1861,13 +1863,20 @@ class orm(orm_template): d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) # all inherited fields + all non inherited fields for which the attribute whose name is in load is True - fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x], '_classic_write'), fields_to_read) + self._inherits.values() + fields_pre = [f for f in fields_to_read if + f == self.CONCURRENCY_CHECK_FIELD + or (f in self._columns and getattr(self._columns[f], '_classic_write')) + ] + self._inherits.values() res = [] if len(fields_pre): def convert_field(f): if f in ('create_date', 'write_date'): return "date_trunc('second', %s) as %s" % (f, f) + if f == self.CONCURRENCY_CHECK_FIELD: + if self._log_access: + return "COALESCE(write_date, create_date, now())::timestamp AS %s" % (f,) + return "now()::timestamp AS %s" % (f,) if isinstance(self._columns[f], fields.binary) and context.get('bin_size', False): return "length(%s) as %s" % (f,f) return '"%s"' % (f,) @@ -1892,6 +1901,8 @@ class orm(orm_template): res = map(lambda x: {'id': x}, ids) for f in fields_pre: + if f == self.CONCURRENCY_CHECK_FIELD: + continue if self._columns[f].translate: ids = map(lambda x: x['id'], res) res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) @@ -2012,26 +2023,33 @@ class orm(orm_template): return res[ids] return res - def unlink(self, cr, uid, ids, context=None): + def _check_concurrency(self, cr, ids, context): if not context: - context = {} + return + if context.get(self.CONCURRENCY_CHECK_FIELD) and self._log_access: + santa = "(id = %s AND %s < COALESCE(write_date, create_date, now())::timestamp)" + for i in range(0, len(ids), cr.IN_MAX): + sub_ids = tools.flatten(((i, context[self.CONCURRENCY_CHECK_FIELD][str(i)]) + for i in ids[i:i+cr.IN_MAX] + if str(i) in context[self.CONCURRENCY_CHECK_FIELD])) + if sub_ids: + cr.execute("SELECT count(1) FROM %s WHERE %s" % (self._table, " OR ".join([santa]*(len(sub_ids)/2))), sub_ids) + res = cr.fetchone() + if res and res[0]: + raise except_orm('ConcurrencyException', _('Records were modified in the meanwhile')) + + del context[self.CONCURRENCY_CHECK_FIELD] + + + def unlink(self, cr, uid, ids, context=None): if not ids: return True if isinstance(ids, (int, long)): ids = [ids] result_store = self._store_get_values(cr, uid, ids, None, context) - delta = context.get('read_delta', False) - if delta and self._log_access: - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] - cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ - "from \"%s\" where id in (%s)" % - (delta, self._table, ",".join(map(str, sub_ids)))) - res = cr.fetchone() - if res and res[0]: - raise except_orm(_('ConcurrencyException'), - _('This record was modified in the meanwhile')) + + self._check_concurrency(cr, ids, context) self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink') @@ -2112,24 +2130,11 @@ class orm(orm_template): return True if isinstance(ids, (int, long)): ids = [ids] - delta = context.get('read_delta', False) - if delta and self._log_access: - for i in range(0, len(ids), cr.IN_MAX): - sub_ids = ids[i:i+cr.IN_MAX] - cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ - "from %s where id in (%s)" % - (delta, self._table, ",".join(map(str, sub_ids)))) - res = cr.fetchone() - if res and res[0]: - for field in vals: - if field in self._columns and self._columns[field]._classic_write: - raise except_orm(_('ConcurrencyException'), - _('This record was modified in the meanwhile')) + + self._check_concurrency(cr, ids, context) self.pool.get('ir.model.access').check(cr, user, self._name, 'write') - #for v in self._inherits.values(): - # assert v not in vals, (v, vals) upd0 = [] upd1 = [] upd_todo = [] @@ -2277,10 +2282,6 @@ class orm(orm_template): ''', (leftbound,rightbound,cwidth,cleft,cright,treeshift,leftbound,rightbound, cwidth,cleft,cright,treeshift,leftrange,rightrange)) - if 'read_delta' in context: - del context['read_delta'] - - result = self._store_get_values(cr, user, ids, vals.keys(), context) for order, object, ids, fields in result: self.pool.get(object)._store_set_values(cr, user, ids, fields, context) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 7b4b5c35156..ab13bae4451 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -518,6 +518,13 @@ wizard() # Report state: # False -> True # + +class ExceptionWithTraceback(Exception): + def __init__(self, msg, tb): + self.message = msg + self.traceback = tb + self.args = (msg, tb) + class report_spool(netsvc.Service): def __init__(self, name='report'): netsvc.Service.__init__(self, name) @@ -553,12 +560,12 @@ class report_spool(netsvc.Service): except Exception, exception: import traceback import sys - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) + tb = sys.exc_info() + tb_s = "".join(traceback.format_exception(*tb)) logger = netsvc.Logger() logger.notifyChannel('web-services', netsvc.LOG_ERROR, 'Exception: %s\n%s' % (str(exception), tb_s)) - self._reports[id]['exception'] = exception + self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb) self._reports[id]['state'] = True cr.close() return True