diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index 01d501fe80f..9abab79e46c 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -228,45 +228,6 @@ class lang(osv.osv): lang() -def original_group(s, grouping, thousands_sep=''): - - if not grouping: - return s, 0 - - result = "" - seps = 0 - spaces = "" - - if s[-1] == ' ': - sp = s.find(' ') - spaces = s[sp:] - s = s[:sp] - - while s and grouping: - # if grouping is -1, we are done - if grouping[0] == -1: - break - # 0: re-use last group ad infinitum - elif grouping[0] != 0: - #process last group - group = grouping[0] - grouping = grouping[1:] - if result: - result = s[-group:] + thousands_sep + result - seps += 1 - else: - result = s[-group:] - s = s[:-group] - if s and s[-1] not in "0123456789": - # the leading string is only spaces and signs - return s + result + spaces, seps - if not result: - return s + spaces, seps - if s: - result = s + thousands_sep + result - seps += 1 - return result + spaces, seps - def split(l, counts): """ @@ -317,52 +278,4 @@ def intersperse(string, counts, separator=''): res = separator.join(map(reverse, reverse(splits))) return left + res + right, len(splits) > 0 and len(splits) -1 or 0 -# TODO rewrite this with a unit test library -def _group_examples(): - for g in [original_group, intersperse]: - # print "asserts on", g.func_name - assert g("", []) == ("", 0) - assert g("0", []) == ("0", 0) - assert g("012", []) == ("012", 0) - assert g("1", []) == ("1", 0) - assert g("12", []) == ("12", 0) - assert g("123", []) == ("123", 0) - assert g("1234", []) == ("1234", 0) - assert g("123456789", []) == ("123456789", 0) - assert g("&ab%#@1", []) == ("&ab%#@1", 0) - - assert g("0", []) == ("0", 0) - assert g("0", [1]) == ("0", 0) - assert g("0", [2]) == ("0", 0) - assert g("0", [200]) == ("0", 0) - - # breaks original_group: - if g.func_name == 'intersperse': - assert g("12345678", [0], '.') == ('12345678', 0) - assert g("", [1], '.') == ('', 0) - assert g("12345678", [1], '.') == ('1234567.8', 1) - assert g("12345678", [1], '.') == ('1234567.8', 1) - assert g("12345678", [2], '.') == ('123456.78', 1) - assert g("12345678", [2,1], '.') == ('12345.6.78', 2) - assert g("12345678", [2,0], '.') == ('12.34.56.78', 3) - assert g("12345678", [-1,2], '.') == ('12345678', 0) - assert g("12345678", [2,-1], '.') == ('123456.78', 1) - assert g("12345678", [2,0,1], '.') == ('12.34.56.78', 3) - assert g("12345678", [2,0,0], '.') == ('12.34.56.78', 3) - assert g("12345678", [2,0,-1], '.') == ('12.34.56.78', 3) - assert g("12345678", [3,3,3,3], '.') == ('12.345.678', 2) - - assert original_group("abc1234567xy", [2], '.') == ('abc1234567.xy', 1) - assert original_group("abc1234567xy8", [2], '.') == ('abc1234567xy8', 0) # difference here... - assert original_group("abc12", [3], '.') == ('abc12', 0) - assert original_group("abc12", [2], '.') == ('abc12', 0) - assert original_group("abc12", [1], '.') == ('abc1.2', 1) - - assert intersperse("abc1234567xy", [2], '.') == ('abc1234567.xy', 1) - assert intersperse("abc1234567xy8", [2], '.') == ('abc1234567x.y8', 1) # ... w.r.t. here. - assert intersperse("abc12", [3], '.') == ('abc12', 0) - assert intersperse("abc12", [2], '.') == ('abc12', 0) - assert intersperse("abc12", [1], '.') == ('abc1.2', 1) - - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/res/test/res_lang.py b/openerp/addons/base/res/test/res_lang.py deleted file mode 100644 index dbfa64f2993..00000000000 --- a/openerp/addons/base/res/test/res_lang.py +++ /dev/null @@ -1,8 +0,0 @@ -import sys -import openerp - -import openerp.addons.base.res.res_lang as res_lang -res_lang._group_examples() - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/test/__init__.py b/openerp/addons/base/test/__init__.py deleted file mode 100644 index c0cc8f7cb78..00000000000 --- a/openerp/addons/base/test/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2011-TODAY OpenERP S.A. -# -# 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 . -# -############################################################################## - -# Useful for manual testing of cron jobs scheduling. -# This must be (un)commented with the corresponding yml file -# in ../__openerp__.py. -# import test_ir_cron - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/tests/__init__.py b/openerp/addons/base/tests/__init__.py index d891bc06fda..5023494172c 100644 --- a/openerp/addons/base/tests/__init__.py +++ b/openerp/addons/base/tests/__init__.py @@ -3,6 +3,7 @@ import test_expression import test_ir_attachment import test_ir_values import test_menu +import test_res_lang import test_search checks = [ @@ -11,5 +12,6 @@ checks = [ test_ir_attachment, test_ir_values, test_menu, + test_res_lang, test_search, ] diff --git a/openerp/addons/base/tests/test_base.py b/openerp/addons/base/tests/test_base.py index 285af8fbf6e..4e1de54920e 100644 --- a/openerp/addons/base/tests/test_base.py +++ b/openerp/addons/base/tests/test_base.py @@ -40,4 +40,4 @@ class test_base(common.TransactionCase): if __name__ == '__main__': - unittest2.main() \ No newline at end of file + unittest2.main() diff --git a/openerp/addons/base/tests/test_res_lang.py b/openerp/addons/base/tests/test_res_lang.py new file mode 100644 index 00000000000..325d1f72065 --- /dev/null +++ b/openerp/addons/base/tests/test_res_lang.py @@ -0,0 +1,43 @@ +import unittest2 + +import openerp.tests.common as common + +class test_res_lang(common.TransactionCase): + + def test_00_intersperse(self): + from openerp.addons.base.res.res_lang import intersperse + + assert intersperse("", []) == ("", 0), "Assert passed" + assert intersperse("0", []) == ("0", 0), "Assert passed" + assert intersperse("012", []) == ("012", 0), "Assert passed" + assert intersperse("1", []) == ("1", 0), "Assert passed" + assert intersperse("12", []) == ("12", 0), "Assert passed" + assert intersperse("123", []) == ("123", 0), "Assert passed" + assert intersperse("1234", []) == ("1234", 0), "Assert passed" + assert intersperse("123456789", []) == ("123456789", 0), "Assert passed" + assert intersperse("&ab%#@1", []) == ("&ab%#@1", 0), "Assert passed" + + assert intersperse("0", []) == ("0", 0), "Assert passed" + assert intersperse("0", [1]) == ("0", 0), "Assert passed" + assert intersperse("0", [2]) == ("0", 0), "Assert passed" + assert intersperse("0", [200]) == ("0", 0), "Assert passed" + + assert intersperse("12345678", [1], '.') == ('1234567.8', 1) + assert intersperse("12345678", [1], '.') == ('1234567.8', 1) + assert intersperse("12345678", [2], '.') == ('123456.78', 1) + assert intersperse("12345678", [2,1], '.') == ('12345.6.78', 2) + assert intersperse("12345678", [2,0], '.') == ('12.34.56.78', 3) + assert intersperse("12345678", [-1,2], '.') == ('12345678', 0) + assert intersperse("12345678", [2,-1], '.') == ('123456.78', 1) + assert intersperse("12345678", [2,0,1], '.') == ('12.34.56.78', 3) + assert intersperse("12345678", [2,0,0], '.') == ('12.34.56.78', 3) + assert intersperse("12345678", [2,0,-1], '.') == ('12.34.56.78', 3) + assert intersperse("12345678", [3,3,3,3], '.') == ('12.345.678', 2) + + assert intersperse("abc1234567xy", [2], '.') == ('abc1234567.xy', 1) + assert intersperse("abc1234567xy8", [2], '.') == ('abc1234567x.y8', 1) # ... w.r.t. here. + assert intersperse("abc12", [3], '.') == ('abc12', 0) + assert intersperse("abc12", [2], '.') == ('abc12', 0) + assert intersperse("abc12", [1], '.') == ('abc1.2', 1) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 98955aff492..ec1c6a1708b 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -400,7 +400,7 @@ def load_openerp_module(module_name): initialize_sys_path() try: mod_path = get_module_path(module_name) - zip_mod_path = mod_path + '.zip' + zip_mod_path = '' if not mod_path else mod_path + '.zip' if not os.path.isfile(zip_mod_path): __import__('openerp.addons.' + module_name) else: diff --git a/openerp/modules/registry.py b/openerp/modules/registry.py index 4aa20f3349d..b06c500a24c 100644 --- a/openerp/modules/registry.py +++ b/openerp/modules/registry.py @@ -216,6 +216,11 @@ class RegistryManager(object): del cls.registries[db_name] raise + # load_modules() above can replace the registry by calling + # indirectly new() again (when modules have to be uninstalled). + # Yeah, crazy. + registry = cls.registries[db_name] + cr = registry.db.cursor() try: Registry.setup_multi_process_signaling(cr) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index b59429ca638..2dc52a2c891 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1077,7 +1077,7 @@ class BaseModel(object): # Validate rec_name if self._rec_name is not None: - assert self._rec_name in self._columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name) + assert self._rec_name in self._all_columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name) else: self._rec_name = 'name' @@ -1362,11 +1362,9 @@ class BaseModel(object): noupdate=noupdate, res_id=id, context=context)) cr.execute('RELEASE SAVEPOINT model_load_save') except psycopg2.Warning, e: - _logger.exception('Failed to import record %s', record) messages.append(dict(info, type='warning', message=str(e))) cr.execute('ROLLBACK TO SAVEPOINT model_load_save') except psycopg2.Error, e: - _logger.exception('Failed to import record %s', record) messages.append(dict( info, type='error', **PGERROR_TO_OE[e.pgcode](self, fg, info, e))) @@ -5328,11 +5326,28 @@ def convert_pgerror_23502(model, fields, info, e): 'message': message, 'field': field_name, } +def convert_pgerror_23505(model, fields, info, e): + m = re.match(r'^duplicate key (?P\w+) violates unique constraint', + str(e)) + field_name = m.group('field') + if not m or field_name not in fields: + return {'message': unicode(e)} + message = _(u"The value for the field '%s' already exists.") % field_name + field = fields.get(field_name) + if field: + message = _(u"%s This might be '%s' in the current model, or a field " + u"of the same name in an o2m.") % (message, field['string']) + return { + 'message': message, + 'field': field_name, + } PGERROR_TO_OE = collections.defaultdict( # shape of mapped converters lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), { # not_null_violation '23502': convert_pgerror_23502, + # unique constraint error + '23505': convert_pgerror_23505, }) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/report/report_sxw.py b/openerp/report/report_sxw.py index b1f1e3d5891..325690205ef 100644 --- a/openerp/report/report_sxw.py +++ b/openerp/report/report_sxw.py @@ -189,6 +189,8 @@ class rml_parse(object): return newtag, attrs def _ellipsis(self, char, size=100, truncation_str='...'): + if not char: + return '' if len(char) <= size: return char return char[:size-len(truncation_str)] + truncation_str diff --git a/openerp/service/workers.py b/openerp/service/workers.py index ed77b28cf1b..6d2da2d927b 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -319,7 +319,13 @@ class WorkerHTTP(Worker): fcntl.fcntl(client, fcntl.F_SETFD, flags) # do request using WorkerBaseWSGIServer monkey patched with socket self.server.socket = client - self.server.process_request(client,addr) + # tolerate broken pipe when the http client closes the socket before + # receiving the full reply + try: + self.server.process_request(client,addr) + except IOError, e: + if e.errno != errno.EPIPE: + raise self.request_count += 1 def process_work(self): diff --git a/openerp/tests/addons/test_impex/ir.model.access.csv b/openerp/tests/addons/test_impex/ir.model.access.csv index 9a674d9ee1f..fd0f7370c67 100644 --- a/openerp/tests/addons/test_impex/ir.model.access.csv +++ b/openerp/tests/addons/test_impex/ir.model.access.csv @@ -23,3 +23,4 @@ access_export_one2many_child_2,access_export_one2many_child_2,model_export_one2m access_export_many2many_other,access_export_many2many_other,model_export_many2many_other,,1,1,1,1 access_export_selection_withdefault,access_export_selection_withdefault,model_export_selection_withdefault,,1,1,1,1 access_export_one2many_recursive,access_export_one2many_recursive,model_export_one2many_recursive,,1,1,1,1 +access_export_unique,access_export_unique,model_export_unique,,1,1,1,1 diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 95a7f90bda8..8c76850d628 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -144,3 +144,13 @@ class RecO2M(orm.Model): 'value': fields.integer(), 'child': fields.one2many('export.one2many.multiple', 'parent_id') } + +class OnlyOne(orm.Model): + _name = 'export.unique' + + _columns = { + 'value': fields.integer(), + } + _sql_constraints = [ + ('value_unique', 'unique (value)', "The value must be unique"), + ] diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index fe01a267d58..0afbf6f49c3 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -1149,3 +1149,29 @@ class test_datetime(ImporterCase): self.assertEqual( values(self.read(domain=[('id', 'in', result['ids'])])), ['2012-02-03 11:11:11']) + +class test_unique(ImporterCase): + model_name = 'export.unique' + + @mute_logger('openerp.sql_db') + def test_unique(self): + result = self.import_(['value'], [ + ['1'], + ['1'], + ['2'], + ['3'], + ['3'], + ]) + self.assertFalse(result['ids']) + self.assertEqual(result['messages'], [ + dict(message=u"The value for the field 'value' already exists. " + u"This might be 'unknown' in the current model, " + u"or a field of the same name in an o2m.", + type='error', rows={'from': 1, 'to': 1}, + record=1, field='value'), + dict(message=u"The value for the field 'value' already exists. " + u"This might be 'unknown' in the current model, " + u"or a field of the same name in an o2m.", + type='error', rows={'from': 4, 'to': 4}, + record=4, field='value'), + ])