From 48b24666768e0f933be806cd3d1b9b51e49c4d71 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Fri, 13 May 2011 11:31:21 +0200 Subject: [PATCH 1/5] [FIX] res_lang: the string to format cannot be empty. - not yet fixed, but a new implementation of the grouping code is provided without that problem. - some testing need to be done. - removed unnecessary arguments - eval() used only once bzr revid: vmt@openerp.com-20110513093121-qo7walg7dff0kufl --- openerp/addons/base/res/res_lang.py | 190 ++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 42 deletions(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index f59127318ec..c01df71792e 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -181,46 +181,6 @@ class lang(osv.osv): trans_obj.unlink(cr, uid, trans_ids, context=context) return super(lang, self).unlink(cr, uid, ids, context=context) - def _group(self, cr, uid, ids, s, monetary=False, grouping=False, thousands_sep=''): - grouping = eval(grouping) - - 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 format(self, cr, uid, ids, percent, value, grouping=False, monetary=False): """ Format() will return the language-specific output for float values""" @@ -228,6 +188,7 @@ class lang(osv.osv): raise ValueError("format() must be given exactly one %char format specifier") lang_grouping, thousands_sep, decimal_point = self._lang_data_get(cr, uid, ids[0], monetary) + eval_lang_grouping = eval(lang_grouping) formatted = percent % value # floats and decimal ints need special action! @@ -236,7 +197,7 @@ class lang(osv.osv): parts = formatted.split('.') if grouping: - parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep) + parts[0], seps = original_group(parts[0], eval_lang_grouping, thousands_sep) formatted = decimal_point.join(parts) while seps: @@ -246,7 +207,7 @@ class lang(osv.osv): seps -= 1 elif percent[-1] in 'diu': if grouping: - formatted = self._group(cr,uid,ids,formatted, monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)[0] + formatted = original_group(formatted, eval_lang_grouping, thousands_sep)[0] return formatted @@ -256,5 +217,150 @@ class lang(osv.osv): lang() +# TODO intersperse is more general than original_group, maybe +# this is a problem and the loop should really stops on non-digits. + +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 + +import itertools + +def take_left_padding(string): + """ + >>> take_left_padding(" hello world ") + (' ', 'hello world ') + + """ + padding = ''.join(itertools.takewhile(lambda c: c.isspace(), string)) + rest = string[len(padding):] + return padding, rest + +def take_right_padding(string): + """ + + >>> take_right_padding(" hello world ") + (' ', ' hello world') + + """ + def reverse(s): return s[::-1] + padding, rest = take_left_padding(reverse(string)) + return reverse(padding), reverse(rest) + +def split(l, counts): + """ + + >>> split("hello world", []) + ['hello world'] + >>> split("hello world", [1]) + ['h', 'ello world'] + >>> split("hello world", [2]) + ['he', 'llo world'] + >>> split("hello world", [2,3]) + ['he', 'llo', ' world'] + >>> split("hello world", [2,3,0]) + ['he', 'llo', ' wo', 'rld'] + >>> split("hello world", [2,-1,3]) + ['he', 'llo world'] + + """ + res = [] + saved_count = len(l) # count to use when encoutering a zero + for count in counts: + if count == -1: + break + if count == 0: + while l: + res.append(l[:saved_count]) + l = l[saved_count:] + break + res.append(l[:count]) + l = l[count:] + saved_count = count + if l: + res.append(l) + return res + +def intersperse(string, counts, separator=''): + """ + + See the assert below for examples. + + """ + left_padding, rest = take_left_padding(string) + right_padding, rest = take_right_padding(rest) + def reverse(s): return s[::-1] + splits = split(reverse(rest), counts) + res = separator.join(map(reverse, reverse(splits))) + return left_padding + res + right_padding, len(splits) > 0 and len(splits) -1 or 0 + +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 _group: + if g.func_name == 'my_group': + assert g("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) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 12144cfc2b8f44d43489a0964e847df4465e552e Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 16 May 2011 11:03:12 +0200 Subject: [PATCH 2/5] [FIX]: dont use the leading non-digits when splitting in the format. bzr revid: vmt@openerp.com-20110516090312-8r5b63e7ln045h0p --- openerp/addons/base/res/res_lang.py | 31 ++++++++++++++++++------ openerp/addons/base/res/test/res_lang.py | 12 +++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 openerp/addons/base/res/test/res_lang.py diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index c01df71792e..c2b4364a240 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -21,6 +21,7 @@ import locale import logging +import itertools from osv import fields, osv from locale import localeconv @@ -259,8 +260,6 @@ def original_group(s, grouping, thousands_sep=''): seps += 1 return result + spaces, seps -import itertools - def take_left_padding(string): """ >>> take_left_padding(" hello world ") @@ -319,17 +318,20 @@ def split(l, counts): def intersperse(string, counts, separator=''): """ - See the assert below for examples. + See the asserts below for examples. """ left_padding, rest = take_left_padding(string) + padding = ''.join(itertools.takewhile(lambda c: not c.isdigit(), rest)) + rest = rest[len(padding):] right_padding, rest = take_right_padding(rest) def reverse(s): return s[::-1] splits = split(reverse(rest), counts) res = separator.join(map(reverse, reverse(splits))) - return left_padding + res + right_padding, len(splits) > 0 and len(splits) -1 or 0 + return left_padding + padding + res + right_padding, len(splits) > 0 and len(splits) -1 or 0 -def _group_examples_(): +# 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) @@ -347,9 +349,9 @@ def _group_examples_(): assert g("0", [2]) == ("0", 0) assert g("0", [200]) == ("0", 0) - # breaks _group: - if g.func_name == 'my_group': - assert g("12345678", [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) @@ -363,4 +365,17 @@ def _group_examples_(): assert g("12345678", [2,0,-1], '.') == ('12.34.56.78', 3) + 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 new file mode 100644 index 00000000000..9272f1142ea --- /dev/null +++ b/openerp/addons/base/res/test/res_lang.py @@ -0,0 +1,12 @@ +import sys +import openerp + +# TODO this loop will be exposed as open_openerp_namespace() +# once trunk-cleaning-vmt is merged. +for k, v in list(sys.modules.items()): + if k.startswith('openerp.') and sys.modules.get(k[8:]) is None: + sys.modules[k[8:]] = v + +import openerp.addons.base.res.res_lang as res_lang +res_lang._group_examples() + From 584fc24bb32d4e6a506ac21996bbe2385d99a5dc Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 16 May 2011 11:09:08 +0200 Subject: [PATCH 3/5] [FIX] forgot to actually replace the function call by the new one. bzr revid: vmt@openerp.com-20110516090908-umx4o2ar2w2igiid --- openerp/addons/base/res/res_lang.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index c2b4364a240..cb1a6883d31 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -198,7 +198,7 @@ class lang(osv.osv): parts = formatted.split('.') if grouping: - parts[0], seps = original_group(parts[0], eval_lang_grouping, thousands_sep) + parts[0], seps = intersperse(parts[0], eval_lang_grouping, thousands_sep) formatted = decimal_point.join(parts) while seps: @@ -208,7 +208,7 @@ class lang(osv.osv): seps -= 1 elif percent[-1] in 'diu': if grouping: - formatted = original_group(formatted, eval_lang_grouping, thousands_sep)[0] + formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0] return formatted @@ -218,9 +218,6 @@ class lang(osv.osv): lang() -# TODO intersperse is more general than original_group, maybe -# this is a problem and the loop should really stops on non-digits. - def original_group(s, grouping, thousands_sep=''): if not grouping: From e248d008c0295a7bae6a0f48fdc39d79032f988e Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 16 May 2011 12:07:12 +0200 Subject: [PATCH 4/5] [IMP] res_lang: using a regex instead of custom function, based on rco comment. bzr revid: vmt@openerp.com-20110516100712-ezg0ul9bo81uto1h --- openerp/addons/base/res/res_lang.py | 31 +++++------------------------ 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index cb1a6883d31..108e65a1575 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -22,6 +22,7 @@ import locale import logging import itertools +import re from osv import fields, osv from locale import localeconv @@ -257,27 +258,6 @@ def original_group(s, grouping, thousands_sep=''): seps += 1 return result + spaces, seps -def take_left_padding(string): - """ - >>> take_left_padding(" hello world ") - (' ', 'hello world ') - - """ - padding = ''.join(itertools.takewhile(lambda c: c.isspace(), string)) - rest = string[len(padding):] - return padding, rest - -def take_right_padding(string): - """ - - >>> take_right_padding(" hello world ") - (' ', ' hello world') - - """ - def reverse(s): return s[::-1] - padding, rest = take_left_padding(reverse(string)) - return reverse(padding), reverse(rest) - def split(l, counts): """ @@ -312,20 +292,19 @@ def split(l, counts): res.append(l) return res +intersperse_pat = re.compile('([^0-9]*)([^ ]*)(.*)') + def intersperse(string, counts, separator=''): """ See the asserts below for examples. """ - left_padding, rest = take_left_padding(string) - padding = ''.join(itertools.takewhile(lambda c: not c.isdigit(), rest)) - rest = rest[len(padding):] - right_padding, rest = take_right_padding(rest) + left, rest, right = intersperse_pat.match(string).groups() def reverse(s): return s[::-1] splits = split(reverse(rest), counts) res = separator.join(map(reverse, reverse(splits))) - return left_padding + padding + res + right_padding, len(splits) > 0 and len(splits) -1 or 0 + return left + res + right, len(splits) > 0 and len(splits) -1 or 0 # TODO rewrite this with a unit test library def _group_examples(): From 059083488ed6411144d2ed075f59621cd8f8f1cc Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Mon, 16 May 2011 12:10:09 +0200 Subject: [PATCH 5/5] [IMP] res_lang: removed unneeded itertools import. bzr revid: vmt@openerp.com-20110516101009-x1ih1y17ci4ihkne --- openerp/addons/base/res/res_lang.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index 108e65a1575..8e2bc68617c 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -21,7 +21,6 @@ import locale import logging -import itertools import re from osv import fields, osv