From 9ffda633889708d5a14a24a9f5e3e486f63fcddb Mon Sep 17 00:00:00 2001 From: Adam Groszer Date: Tue, 19 Feb 2019 12:32:53 +0100 Subject: Fix `NumberFormat` to respect the thousand grouping given by the pattern. fixes #38 --- src/zope/i18n/format.py | 59 +++++-- src/zope/i18n/tests/test_formats.py | 318 ++++++++++++++++++++++++------------ 2 files changed, 266 insertions(+), 111 deletions(-) (limited to 'src') diff --git a/src/zope/i18n/format.py b/src/zope/i18n/format.py index 96f5ef3..abc3e50 100644 --- a/src/zope/i18n/format.py +++ b/src/zope/i18n/format.py @@ -226,7 +226,6 @@ class NumberParseError(Exception): class NumberFormat(object): __doc__ = INumberFormat.__doc__ - type = None _pattern = None _bin_pattern = None @@ -368,6 +367,33 @@ class NumberFormat(object): fraction = self.symbols['decimal'] + fraction return fraction, roundInt + # taken from cpython lib/Locale.py + def _grouping_intervals(self, grouping): + last_interval = None + for interval in grouping: + # 0: re-use last group ad infinitum + if interval == 0: + if last_interval is None: + raise ValueError("invalid grouping") + while True: + yield last_interval + yield interval + last_interval = interval + + def _group(self, integer, grouping): + # take a given chunk of digits and insert the group symbol + # grouping is usually: (3, 0) or (3, 2, 0) + digits = list(reversed(integer)) + last_idx = 0 + for group_length in self._grouping_intervals(grouping): + pos = last_idx + group_length + if pos >= len(digits): + break + digits.insert(pos, self.symbols['group']) + last_idx = pos + 1 + res = ''.join(reversed(digits)) + return res + def format(self, obj, pattern=None, rounding=True): "See zope.i18n.interfaces.IFormat" # Make or get binary form of datetime pattern @@ -454,13 +480,8 @@ class NumberFormat(object): integer = self._format_integer(str(int(math.fabs(obj))), bin_pattern[INTEGER]) # Adding grouping - if bin_pattern[GROUPING] == 1: - help = '' - for pos in range(1, len(integer)+1): - if (pos-1)%3 == 0 and pos != 1: - help = self.symbols['group'] + help - help = integer[-pos] + help - integer = help + if bin_pattern[GROUPING]: + integer = self._group(integer, bin_pattern[GROUPING]) pre_padding = len(bin_pattern[INTEGER]) - len(integer) post_padding = len(bin_pattern[FRACTION]) - len(fraction)+1 number = integer + fraction @@ -779,12 +800,10 @@ def parseNumberPattern(pattern): fraction = '' exponential = '' suffix = '' - grouping = 0 neg_pattern = None SPECIALCHARS = "*.,#0;E'" - length = len(pattern) state = BEGIN helper = '' for pos, char in enumerate(pattern): @@ -832,7 +851,8 @@ def parseNumberPattern(pattern): if char == "#" or char == "0": helper += char elif char == ",": - grouping = 1 + # just add grouping markers to the integer pattern + helper += char elif char == ".": integer = helper helper = '' @@ -934,6 +954,23 @@ def parseNumberPattern(pattern): if state == READ_EXPONENTIAL: exponential = helper + # the integer pattern can have the grouping delimiters too, let's take care + # about those here and now + # convert to a tuple of length of groups, from right to left + # example: (3, 0) for the usual triple separated, (3, 2, 0) for Hindi + # practically trying to return the same as locale.localeconv()['grouping'] + grouping = () + if "," in integer: + last_index = -1 + for index, char in enumerate(reversed(integer)): + if char == ",": + grouping += (index-last_index-1,) + last_index = index + # use last group ad infinitum + grouping += (0,) + # remove grouping markers from integer pattern + integer = integer.replace(",", "") + pattern = (padding_1, prefix, padding_2, integer, fraction, exponential, padding_3, suffix, padding_4, grouping) diff --git a/src/zope/i18n/tests/test_formats.py b/src/zope/i18n/tests/test_formats.py index 99c4bf7..879a71a 100644 --- a/src/zope/i18n/tests/test_formats.py +++ b/src/zope/i18n/tests/test_formats.py @@ -694,247 +694,349 @@ class TestNumberPatternParser(_TestCase): def testParseSimpleIntegerPattern(self): self.assertEqual( parseNumberPattern('###0'), - ((None, '', None, '###0', '', '', None, '', None, 0), - (None, '', None, '###0', '', '', None, '', None, 0))) + ((None, '', None, '###0', '', '', None, '', None, ()), + (None, '', None, '###0', '', '', None, '', None, ()))) def testParseScientificIntegerPattern(self): self.assertEqual( parseNumberPattern('###0E#0'), - ((None, '', None, '###0', '', '#0', None, '', None, 0), - (None, '', None, '###0', '', '#0', None, '', None, 0))) + ((None, '', None, '###0', '', '#0', None, '', None, ()), + (None, '', None, '###0', '', '#0', None, '', None, ()))) self.assertEqual( parseNumberPattern('###0E+#0'), - ((None, '', None, '###0', '', '+#0', None, '', None, 0), - (None, '', None, '###0', '', '+#0', None, '', None, 0))) + ((None, '', None, '###0', '', '+#0', None, '', None, ()), + (None, '', None, '###0', '', '+#0', None, '', None, ()))) def testParsePosNegAlternativeIntegerPattern(self): self.assertEqual( parseNumberPattern('###0;#0'), - ((None, '', None, '###0', '', '', None, '', None, 0), - (None, '', None, '#0', '', '', None, '', None, 0))) + ((None, '', None, '###0', '', '', None, '', None, ()), + (None, '', None, '#0', '', '', None, '', None, ()))) def testParsePrefixedIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0'), - ((None, '+', None, '###0', '', '', None, '', None, 0), - (None, '+', None, '###0', '', '', None, '', None, 0))) + ((None, '+', None, '###0', '', '', None, '', None, ()), + (None, '+', None, '###0', '', '', None, '', None, ()))) def testParsePosNegIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0;-###0'), - ((None, '+', None, '###0', '', '', None, '', None, 0), - (None, '-', None, '###0', '', '', None, '', None, 0))) + ((None, '+', None, '###0', '', '', None, '', None, ()), + (None, '-', None, '###0', '', '', None, '', None, ()))) def testParseScientificPosNegIntegerPattern(self): self.assertEqual( parseNumberPattern('+###0E0;-###0E#0'), - ((None, '+', None, '###0', '', '0', None, '', None, 0), - (None, '-', None, '###0', '', '#0', None, '', None, 0))) + ((None, '+', None, '###0', '', '0', None, '', None, ()), + (None, '-', None, '###0', '', '#0', None, '', None, ()))) def testParseThousandSeparatorIntegerPattern(self): self.assertEqual( parseNumberPattern('#,##0'), - ((None, '', None, '###0', '', '', None, '', None, 1), - (None, '', None, '###0', '', '', None, '', None, 1))) + ((None, '', None, '###0', '', '', None, '', None, (3, 0)), + (None, '', None, '###0', '', '', None, '', None, (3, 0)))) def testParseSimpleDecimalPattern(self): self.assertEqual( parseNumberPattern('###0.00#'), - ((None, '', None, '###0', '00#', '', None, '', None, 0), - (None, '', None, '###0', '00#', '', None, '', None, 0))) + ((None, '', None, '###0', '00#', '', None, '', None, ()), + (None, '', None, '###0', '00#', '', None, '', None, ()))) def testParseScientificDecimalPattern(self): self.assertEqual( parseNumberPattern('###0.00#E#0'), - ((None, '', None, '###0', '00#', '#0', None, '', None, 0), - (None, '', None, '###0', '00#', '#0', None, '', None, 0))) + ((None, '', None, '###0', '00#', '#0', None, '', None, ()), + (None, '', None, '###0', '00#', '#0', None, '', None, ()))) def testParsePosNegAlternativeFractionPattern(self): self.assertEqual( parseNumberPattern('###0.00#;#0.0#'), - ((None, '', None, '###0', '00#', '', None, '', None, 0), - (None, '', None, '#0', '0#', '', None, '', None, 0))) + ((None, '', None, '###0', '00#', '', None, '', None, ()), + (None, '', None, '#0', '0#', '', None, '', None, ()))) def testParsePosNegFractionPattern(self): self.assertEqual( parseNumberPattern('+###0.0##;-###0.0##'), - ((None, '+', None, '###0', '0##', '', None, '', None, 0), - (None, '-', None, '###0', '0##', '', None, '', None, 0))) + ((None, '+', None, '###0', '0##', '', None, '', None, ()), + (None, '-', None, '###0', '0##', '', None, '', None, ()))) def testParseScientificPosNegFractionPattern(self): self.assertEqual( parseNumberPattern('+###0.0##E#0;-###0.0##E0'), - ((None, '+', None, '###0', '0##', '#0', None, '', None, 0), - (None, '-', None, '###0', '0##', '0', None, '', None, 0))) + ((None, '+', None, '###0', '0##', '#0', None, '', None, ()), + (None, '-', None, '###0', '0##', '0', None, '', None, ()))) def testParseThousandSeparatorFractionPattern(self): self.assertEqual( parseNumberPattern('#,##0.0#'), - ((None, '', None, '###0', '0#', '', None, '', None, 1), - (None, '', None, '###0', '0#', '', None, '', None, 1))) + ((None, '', None, '###0', '0#', '', None, '', None, (3, 0)), + (None, '', None, '###0', '0#', '', None, '', None, (3, 0)))) + + def testParseThousandSeparatorPatterns(self): + # the following patterns are present in the ICU XMLs: + self.assertEqual( + parseNumberPattern('#,##0.00;-#,##0.00'), + ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##,##0.###;-#,##,##0.###'), + ((None, '', None, '#####0', '###', '', None, '', None, (3, 2, 0)), + (None, '-', None, '#####0', '###', '', None, '', None, (3, 2, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.##;-#,##0.##'), + ((None, '', None, '###0', '##', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '##', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.###;-#,##0.###'), + ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '###', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.###;(#,##0.###)'), + ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '(', None, '###0', '###', '', None, ')', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.###;#,##0.###-'), + ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '', None, '###0', '###', '', None, '-', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('##,##,##0.###;-##,##,##0.###'), + ((None, '', None, '######0', '###', '', None, '', None, (3, 2, 0)), + (None, '-', None, '######0', '###', '', None, '', None, (3, 2, 0)))) + + self.assertEqual( + parseNumberPattern('##,##0.##;-##,##0.##'), + ((None, '', None, '####0', '##', '', None, '', None, (3, 0)), + (None, '-', None, '####0', '##', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('###0.###;-###0.###'), + ((None, '', None, '###0', '###', '', None, '', None, ()), + (None, '-', None, '###0', '###', '', None, '', None, ()))) + + self.assertEqual( + parseNumberPattern('###0.###;###0.###-'), + ((None, '', None, '###0', '###', '', None, '', None, ()), + (None, '', None, '###0', '###', '', None, '-', None, ()))) + + self.assertEqual( + parseNumberPattern('#0.###;-#0.###'), + ((None, '', None, '#0', '###', '', None, '', None, ()), + (None, '-', None, '#0', '###', '', None, '', None, ()))) + + self.assertEqual( + parseNumberPattern('#,##0.###;#,##0.###'), + ((None, '', None, '###0', '###', '', None, '', None, (3, 0)), + (None, '', None, '###0', '###', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('###0.###;-###0.###'), + ((None, '', None, '###0', '###', '', None, '', None, ()), + (None, '-', None, '###0', '###', '', None, '', None, ()))) + + self.assertEqual( + parseNumberPattern('#,##0.00;-#,##0.00'), + ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.00;(#,##0.00)'), + ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '(', None, '###0', '00', '', None, ')', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('#,##0.00;-#,##0.00'), + ((None, '', None, '###0', '00', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '00', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('##,##,##0.00;-##,##,##0.00'), + ((None, '', None, '######0', '00', '', None, '', None, (3, 2, 0)), + (None, '-', None, '######0', '00', '', None, '', None, (3, 2, 0)))) + + self.assertEqual( + parseNumberPattern('###0.00;-###0.00'), + ((None, '', None, '###0', '00', '', None, '', None, ()), + (None, '-', None, '###0', '00', '', None, '', None, ()))) + + self.assertEqual( + parseNumberPattern('#,##0;-#,##0'), + ((None, '', None, '###0', '', '', None, '', None, (3, 0)), + (None, '-', None, '###0', '', '', None, '', None, (3, 0)))) + + self.assertEqual( + parseNumberPattern('###0.00;-###0.00'), + ((None, '', None, '###0', '00', '', None, '', None, ()), + (None, '-', None, '###0', '00', '', None, '', None, ()))) def testParsePadding1WithoutPrefixPattern(self): self.assertEqual( parseNumberPattern('* ###0'), - ((' ', '', None, '###0', '', '', None, '', None, 0), - (' ', '', None, '###0', '', '', None, '', None, 0))) + ((' ', '', None, '###0', '', '', None, '', None, ()), + (' ', '', None, '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* ###0.0##'), - ((' ', '', None, '###0', '0##', '', None, '', None, 0), - (' ', '', None, '###0', '0##', '', None, '', None, 0))) + ((' ', '', None, '###0', '0##', '', None, '', None, ()), + (' ', '', None, '###0', '0##', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* ###0.0##;*_###0.0##'), - ((' ', '', None, '###0', '0##', '', None, '', None, 0), - ('_', '', None, '###0', '0##', '', None, '', None, 0))) + ((' ', '', None, '###0', '0##', '', None, '', None, ()), + ('_', '', None, '###0', '0##', '', None, '', None, ()))) def testParsePadding1WithPrefixPattern(self): self.assertEqual( parseNumberPattern('* +###0'), - ((' ', '+', None, '###0', '', '', None, '', None, 0), - (' ', '+', None, '###0', '', '', None, '', None, 0))) + ((' ', '+', None, '###0', '', '', None, '', None, ()), + (' ', '+', None, '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* +###0.0##'), - ((' ', '+', None, '###0', '0##', '', None, '', None, 0), - (' ', '+', None, '###0', '0##', '', None, '', None, 0))) + ((' ', '+', None, '###0', '0##', '', None, '', None, ()), + (' ', '+', None, '###0', '0##', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* +###0.0##;*_-###0.0##'), - ((' ', '+', None, '###0', '0##', '', None, '', None, 0), - ('_', '-', None, '###0', '0##', '', None, '', None, 0))) + ((' ', '+', None, '###0', '0##', '', None, '', None, ()), + ('_', '-', None, '###0', '0##', '', None, '', None, ()))) def testParsePadding1Padding2WithPrefixPattern(self): self.assertEqual( parseNumberPattern('* +* ###0'), - ((' ', '+', ' ', '###0', '', '', None, '', None, 0), - (' ', '+', ' ', '###0', '', '', None, '', None, 0))) + ((' ', '+', ' ', '###0', '', '', None, '', None, ()), + (' ', '+', ' ', '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* +* ###0.0##'), - ((' ', '+', ' ', '###0', '0##', '', None, '', None, 0), - (' ', '+', ' ', '###0', '0##', '', None, '', None, 0))) + ((' ', '+', ' ', '###0', '0##', '', None, '', None, ()), + (' ', '+', ' ', '###0', '0##', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('* +* ###0.0##;*_-*_###0.0##'), - ((' ', '+', ' ', '###0', '0##', '', None, '', None, 0), - ('_', '-', '_', '###0', '0##', '', None, '', None, 0))) + ((' ', '+', ' ', '###0', '0##', '', None, '', None, ()), + ('_', '-', '_', '###0', '0##', '', None, '', None, ()))) - def testParsePadding3WithoutSufffixPattern(self): + def testParsePadding3WithoutSuffixPattern(self): self.assertEqual( parseNumberPattern('###0* '), - ((None, '', None, '###0', '', '', ' ', '', None, 0), - (None, '', None, '###0', '', '', ' ', '', None, 0))) + ((None, '', None, '###0', '', '', ' ', '', None, ()), + (None, '', None, '###0', '', '', ' ', '', None, ()))) self.assertEqual( parseNumberPattern('###0.0##* '), - ((None, '', None, '###0', '0##', '', ' ', '', None, 0), - (None, '', None, '###0', '0##', '', ' ', '', None, 0))) + ((None, '', None, '###0', '0##', '', ' ', '', None, ()), + (None, '', None, '###0', '0##', '', ' ', '', None, ()))) self.assertEqual( parseNumberPattern('###0.0##* ;###0.0##*_'), - ((None, '', None, '###0', '0##', '', ' ', '', None, 0), - (None, '', None, '###0', '0##', '', '_', '', None, 0))) + ((None, '', None, '###0', '0##', '', ' ', '', None, ()), + (None, '', None, '###0', '0##', '', '_', '', None, ()))) def testParsePadding3InScientificPattern(self): self.assertEqual( parseNumberPattern('###0E#0* '), - ((None, '', None, '###0', '', '#0', ' ', '', None, 0), - (None, '', None, '###0', '', '#0', ' ', '', None, 0))) + ((None, '', None, '###0', '', '#0', ' ', '', None, ()), + (None, '', None, '###0', '', '#0', ' ', '', None, ()))) self.assertEqual( parseNumberPattern('###0.0##E0* '), - ((None, '', None, '###0', '0##', '0', ' ', '', None, 0), - (None, '', None, '###0', '0##', '0', ' ', '', None, 0))) + ((None, '', None, '###0', '0##', '0', ' ', '', None, ()), + (None, '', None, '###0', '0##', '0', ' ', '', None, ()))) self.assertEqual( parseNumberPattern('###0.0##E#0* ;###0.0##E0*_'), - ((None, '', None, '###0', '0##', '#0', ' ', '', None, 0), - (None, '', None, '###0', '0##', '0', '_', '', None, 0))) + ((None, '', None, '###0', '0##', '#0', ' ', '', None, ()), + (None, '', None, '###0', '0##', '0', '_', '', None, ()))) def testParsePadding3WithSufffixPattern(self): self.assertEqual( parseNumberPattern('###0* /'), - ((None, '', None, '###0', '', '', ' ', '/', None, 0), - (None, '', None, '###0', '', '', ' ', '/', None, 0))) + ((None, '', None, '###0', '', '', ' ', '/', None, ()), + (None, '', None, '###0', '', '', ' ', '/', None, ()))) self.assertEqual( parseNumberPattern('###0.0#* /'), - ((None, '', None, '###0', '0#', '', ' ', '/', None, 0), - (None, '', None, '###0', '0#', '', ' ', '/', None, 0))) + ((None, '', None, '###0', '0#', '', ' ', '/', None, ()), + (None, '', None, '###0', '0#', '', ' ', '/', None, ()))) self.assertEqual( parseNumberPattern('###0.0#* /;###0.0#*_/'), - ((None, '', None, '###0', '0#', '', ' ', '/', None, 0), - (None, '', None, '###0', '0#', '', '_', '/', None, 0))) + ((None, '', None, '###0', '0#', '', ' ', '/', None, ()), + (None, '', None, '###0', '0#', '', '_', '/', None, ()))) def testParsePadding3And4WithSuffixPattern(self): self.assertEqual( parseNumberPattern('###0* /* '), - ((None, '', None, '###0', '', '', ' ', '/', ' ', 0), - (None, '', None, '###0', '', '', ' ', '/', ' ', 0))) + ((None, '', None, '###0', '', '', ' ', '/', ' ', ()), + (None, '', None, '###0', '', '', ' ', '/', ' ', ()))) self.assertEqual( parseNumberPattern('###0* /* ;###0*_/*_'), - ((None, '', None, '###0', '', '', ' ', '/', ' ', 0), - (None, '', None, '###0', '', '', '_', '/', '_', 0))) + ((None, '', None, '###0', '', '', ' ', '/', ' ', ()), + (None, '', None, '###0', '', '', '_', '/', '_', ()))) def testParseMultipleCharacterPrefix(self): self.assertEqual( parseNumberPattern('DM###0'), - ((None, 'DM', None, '###0', '', '', None, '', None, 0), - (None, 'DM', None, '###0', '', '', None, '', None, 0))) + ((None, 'DM', None, '###0', '', '', None, '', None, ()), + (None, 'DM', None, '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern('DM* ###0'), - ((None, 'DM', ' ', '###0', '', '', None, '', None, 0), - (None, 'DM', ' ', '###0', '', '', None, '', None, 0))) + ((None, 'DM', ' ', '###0', '', '', None, '', None, ()), + (None, 'DM', ' ', '###0', '', '', None, '', None, ()))) def testParseStringEscapedPrefix(self): self.assertEqual( parseNumberPattern("'DEM'###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, 0), - (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + ((None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern("D'EM'###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, 0), - (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + ((None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()))) self.assertEqual( parseNumberPattern("D'E'M###0"), - ((None, 'DEM', None, '###0', '', '', None, '', None, 0), - (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + ((None, 'DEM', None, '###0', '', '', None, '', None, ()), + (None, 'DEM', None, '###0', '', '', None, '', None, ()))) def testParseStringEscapedSuffix(self): self.assertEqual( parseNumberPattern("###0'DEM'"), - ((None, '', None, '###0', '', '', None, 'DEM', None, 0), - (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + ((None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()))) self.assertEqual( parseNumberPattern("###0D'EM'"), - ((None, '', None, '###0', '', '', None, 'DEM', None, 0), - (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + ((None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()))) self.assertEqual( parseNumberPattern("###0D'E'M"), - ((None, '', None, '###0', '', '', None, 'DEM', None, 0), - (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + ((None, '', None, '###0', '', '', None, 'DEM', None, ()), + (None, '', None, '###0', '', '', None, 'DEM', None, ()))) def testParseInvalidBegin(self): with self.assertRaisesRegex(NumberPatternParseError, "Wrong syntax at beginning"): parseNumberPattern(".") - def testParseFractionQuate(self): + def testParseFractionQuote(self): pattern, neg_pattern = parseNumberPattern("0.'") self.assertEqual( - (None, '', None, '0', '', '', None, '', None, 0), + (None, '', None, '0', '', '', None, '', None, ()), pattern) self.assertEqual( - (None, '', None, '0', '', '', None, '', None, 0), + (None, '', None, '0', '', '', None, '', None, ()), neg_pattern) def testParseExponentialQuote(self): pattern, neg_pattern = parseNumberPattern("0E'") self.assertEqual( - (None, '', None, '0', '', '', None, '', None, 0), + (None, '', None, '0', '', '', None, '', None, ()), pattern) self.assertEqual( - (None, '', None, '0', '', '', None, '', None, 0), + (None, '', None, '0', '', '', None, '', None, ()), neg_pattern) def testParseExponentialNumber(self): pattern, neg_pattern = parseNumberPattern("0E1") self.assertEqual( - (None, '', None, '0', '', '', None, '1', None, 0), + (None, '', None, '0', '', '', None, '1', None, ()), pattern) self.assertEqual( - (None, '', None, '0', '', '', None, '1', None, 0), + (None, '', None, '0', '', '', None, '1', None, ()), neg_pattern) class TestNumberFormat(_TestCase): @@ -1199,14 +1301,30 @@ class TestNumberFormat(_TestCase): '-2.3341E04') def testFormatThousandSeparatorInteger(self): - self.assertEqual(self.format.format(23341, '+#,##0;-#,##0'), - '+23,341') - self.assertEqual(self.format.format(-23341, '+#,##0;-#,##0'), - '-23,341') - self.assertEqual(self.format.format(41, '+#0,000;-#0,000'), - '+0,041') - self.assertEqual(self.format.format(-41, '+#0,000;-#0,000'), - '-0,041') + self.assertEqual( + self.format.format(23341, '+#,##0;-#,##0'), + '+23,341') + self.assertEqual( + self.format.format(-23341, '+#,##0;-#,##0'), + '-23,341') + self.assertEqual( + self.format.format(41, '+#0,000;-#0,000'), + '+0,041') + self.assertEqual( + self.format.format(-41, '+#0,000;-#0,000'), + '-0,041') + self.assertEqual( + self.format.format(987654321, '+#,##0;-#,##0'), + '+987,654,321') + self.assertEqual( + self.format.format(-987654321, '+#,##0;-#,##0'), + '-987,654,321') + self.assertEqual( + self.format.format(987654321, '+#,##,#0,000;-#,##,#0,000'), + '+98,76,54,321') + self.assertEqual( + self.format.format(-987654321, '+#,##,#0,000;-#,##,#0,000'), + '-98,76,54,321') def testFormatDecimal(self): self.assertEqual(self.format.format(23341.02357, '###0.0#'), -- cgit v1.2.1