Kaydet (Commit) a099d145 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Merged revisions 70356 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r70356 | antoine.pitrou | 2009-03-14 01:07:21 +0100 (sam., 14 mars 2009) | 3 lines

  Issue #1222: locale.format() bug when the thousands separator is a space character.
........
üst 7fd6068c
...@@ -108,6 +108,19 @@ def localeconv(): ...@@ -108,6 +108,19 @@ def localeconv():
# Author: Martin von Loewis # Author: Martin von Loewis
# improved by Georg Brandl # improved by Georg Brandl
# Iterate over grouping intervals
def _grouping_intervals(grouping):
for interval in grouping:
# if grouping is -1, we are done
if interval == CHAR_MAX:
return
# 0: re-use last group ad infinitum
if interval == 0:
while True:
yield last_interval
yield interval
last_interval = interval
#perform the grouping from right to left #perform the grouping from right to left
def _group(s, monetary=False): def _group(s, monetary=False):
conv = localeconv() conv = localeconv()
...@@ -117,35 +130,41 @@ def _group(s, monetary=False): ...@@ -117,35 +130,41 @@ def _group(s, monetary=False):
return (s, 0) return (s, 0)
result = "" result = ""
seps = 0 seps = 0
spaces = ""
if s[-1] == ' ': if s[-1] == ' ':
sp = s.find(' ') stripped = s.rstrip()
spaces = s[sp:] right_spaces = s[len(stripped):]
s = s[:sp] s = stripped
while s and grouping:
# if grouping is -1, we are done
if grouping[0] == CHAR_MAX:
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: else:
result = s[-group:] right_spaces = ''
s = s[:-group] left_spaces = ''
if s and s[-1] not in "0123456789": groups = []
# the leading string is only spaces and signs for interval in _grouping_intervals(grouping):
return s + result + spaces, seps if not s or s[-1] not in "0123456789":
if not result: # only non-digit characters remain (sign, spaces)
return s + spaces, seps left_spaces = s
s = ''
break
groups.append(s[-interval:])
s = s[:-interval]
if s: if s:
result = s + thousands_sep + result groups.append(s)
seps += 1 groups.reverse()
return result + spaces, seps return (
left_spaces + thousands_sep.join(groups) + right_spaces,
len(groups) - 1
)
# Strip a given amount of excess padding from the given string
def _strip_padding(s, amount):
lpos = 0
while amount and s[lpos] == ' ':
lpos += 1
amount -= 1
rpos = len(s) - 1
while amount and s[rpos] == ' ':
rpos -= 1
amount -= 1
return s[lpos:rpos+1]
def format(percent, value, grouping=False, monetary=False, *additional): def format(percent, value, grouping=False, monetary=False, *additional):
"""Returns the locale-aware substitution of a %? specifier """Returns the locale-aware substitution of a %? specifier
...@@ -170,14 +189,14 @@ def format(percent, value, grouping=False, monetary=False, *additional): ...@@ -170,14 +189,14 @@ def format(percent, value, grouping=False, monetary=False, *additional):
decimal_point = localeconv()[monetary and 'mon_decimal_point' decimal_point = localeconv()[monetary and 'mon_decimal_point'
or 'decimal_point'] or 'decimal_point']
formatted = decimal_point.join(parts) formatted = decimal_point.join(parts)
while seps: if seps:
sp = formatted.find(' ') formatted = _strip_padding(formatted, seps)
if sp == -1: break
formatted = formatted[:sp] + formatted[sp+1:]
seps -= 1
elif percent[-1] in 'diu': elif percent[-1] in 'diu':
seps = 0
if grouping: if grouping:
formatted = _group(formatted, monetary=monetary)[0] formatted, seps = _group(formatted, monetary=monetary)
if seps:
formatted = _strip_padding(formatted, seps)
return formatted return formatted
import re, operator import re, operator
......
...@@ -105,6 +105,32 @@ class EnUSCookedTest(BaseCookedTest): ...@@ -105,6 +105,32 @@ class EnUSCookedTest(BaseCookedTest):
} }
class FrFRCookedTest(BaseCookedTest):
# A cooked "fr_FR" locale with a space character as decimal separator
# and a non-ASCII currency symbol.
cooked_values = {
'currency_symbol': '\xe2\x82\xac',
'decimal_point': ',',
'frac_digits': 2,
'grouping': [3, 3, 0],
'int_curr_symbol': 'EUR ',
'int_frac_digits': 2,
'mon_decimal_point': ',',
'mon_grouping': [3, 3, 0],
'mon_thousands_sep': ' ',
'n_cs_precedes': 0,
'n_sep_by_space': 1,
'n_sign_posn': 1,
'negative_sign': '-',
'p_cs_precedes': 0,
'p_sep_by_space': 1,
'p_sign_posn': 1,
'positive_sign': '',
'thousands_sep': ' '
}
class BaseFormattingTest(object): class BaseFormattingTest(object):
# #
# Utility functions for formatting tests # Utility functions for formatting tests
...@@ -152,6 +178,12 @@ class EnUSNumberFormatting(BaseFormattingTest): ...@@ -152,6 +178,12 @@ class EnUSNumberFormatting(BaseFormattingTest):
self._test_format("%+d", 4200, grouping=True, out='+4%s200' % self.sep) self._test_format("%+d", 4200, grouping=True, out='+4%s200' % self.sep)
self._test_format("%+d", -4200, grouping=True, out='-4%s200' % self.sep) self._test_format("%+d", -4200, grouping=True, out='-4%s200' % self.sep)
def test_integer_grouping_and_padding(self):
self._test_format("%10d", 4200, grouping=True,
out=('4%s200' % self.sep).rjust(10))
self._test_format("%-10d", -4200, grouping=True,
out=('-4%s200' % self.sep).ljust(10))
def test_simple(self): def test_simple(self):
self._test_format("%f", 1024, grouping=0, out='1024.000000') self._test_format("%f", 1024, grouping=0, out='1024.000000')
self._test_format("%f", 102, grouping=0, out='102.000000') self._test_format("%f", 102, grouping=0, out='102.000000')
...@@ -223,6 +255,49 @@ class TestCNumberFormatting(CCookedTest, BaseFormattingTest): ...@@ -223,6 +255,49 @@ class TestCNumberFormatting(CCookedTest, BaseFormattingTest):
self._test_format("%9.2f", 12345.67, grouping=True, out=' 12345.67') self._test_format("%9.2f", 12345.67, grouping=True, out=' 12345.67')
class TestFrFRNumberFormatting(FrFRCookedTest, BaseFormattingTest):
# Test number formatting with a cooked "fr_FR" locale.
def test_decimal_point(self):
self._test_format("%.2f", 12345.67, out='12345,67')
def test_grouping(self):
self._test_format("%.2f", 345.67, grouping=True, out='345,67')
self._test_format("%.2f", 12345.67, grouping=True, out='12 345,67')
def test_grouping_and_padding(self):
self._test_format("%6.2f", 345.67, grouping=True, out='345,67')
self._test_format("%7.2f", 345.67, grouping=True, out=' 345,67')
self._test_format("%8.2f", 12345.67, grouping=True, out='12 345,67')
self._test_format("%9.2f", 12345.67, grouping=True, out='12 345,67')
self._test_format("%10.2f", 12345.67, grouping=True, out=' 12 345,67')
self._test_format("%-6.2f", 345.67, grouping=True, out='345,67')
self._test_format("%-7.2f", 345.67, grouping=True, out='345,67 ')
self._test_format("%-8.2f", 12345.67, grouping=True, out='12 345,67')
self._test_format("%-9.2f", 12345.67, grouping=True, out='12 345,67')
self._test_format("%-10.2f", 12345.67, grouping=True, out='12 345,67 ')
def test_integer_grouping(self):
self._test_format("%d", 200, grouping=True, out='200')
self._test_format("%d", 4200, grouping=True, out='4 200')
def test_integer_grouping_and_padding(self):
self._test_format("%4d", 4200, grouping=True, out='4 200')
self._test_format("%5d", 4200, grouping=True, out='4 200')
self._test_format("%10d", 4200, grouping=True, out='4 200'.rjust(10))
self._test_format("%-4d", 4200, grouping=True, out='4 200')
self._test_format("%-5d", 4200, grouping=True, out='4 200')
self._test_format("%-10d", 4200, grouping=True, out='4 200'.ljust(10))
def test_currency(self):
euro = u'\u20ac'.encode('utf-8')
self._test_currency(50000, "50000,00 " + euro)
self._test_currency(50000, "50 000,00 " + euro, grouping=True)
# XXX is the trailing space a bug?
self._test_currency(50000, "50 000,00 EUR ",
grouping=True, international=True)
class TestStringMethods(BaseLocalizedTest): class TestStringMethods(BaseLocalizedTest):
locale_type = locale.LC_CTYPE locale_type = locale.LC_CTYPE
...@@ -277,7 +352,8 @@ def test_main(): ...@@ -277,7 +352,8 @@ def test_main():
tests = [ tests = [
TestMiscellaneous, TestMiscellaneous,
TestEnUSNumberFormatting, TestEnUSNumberFormatting,
TestCNumberFormatting TestCNumberFormatting,
TestFrFRNumberFormatting,
] ]
# TestSkipped can't be raised inside unittests, handle it manually instead # TestSkipped can't be raised inside unittests, handle it manually instead
try: try:
......
...@@ -89,6 +89,9 @@ Core and Builtins ...@@ -89,6 +89,9 @@ Core and Builtins
Library Library
------- -------
- Issue #1222: locale.format() bug when the thousands separator is a space
character.
- Issue #4792: Prevent a segfault in _tkinter by using the - Issue #4792: Prevent a segfault in _tkinter by using the
guaranteed to be safe interp argument given to the PythonCmd in place of guaranteed to be safe interp argument given to the PythonCmd in place of
the Tcl interpreter taken from a PythonCmd_ClientData. the Tcl interpreter taken from a PythonCmd_ClientData.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment