]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
plural.extract_operands function and tests
authorbenselme <benselme@gmail.com>
Fri, 9 Jan 2015 15:33:24 +0000 (10:33 -0500)
committerbenselme <benselme@gmail.com>
Fri, 9 Jan 2015 15:33:24 +0000 (10:33 -0500)
babel/plural.py
tests/test_plural.py

index ce6659b477c6054c3794eaeae3e90da032e7200b..b11b17e512309cfe9ad919c6526c25d52a63549c 100644 (file)
@@ -8,7 +8,7 @@
     :copyright: (c) 2013 by the Babel Team.
     :license: BSD, see LICENSE for more details.
 """
-
+import decimal
 import re
 
 
@@ -16,6 +16,32 @@ _plural_tags = ('zero', 'one', 'two', 'few', 'many', 'other')
 _fallback_tag = 'other'
 
 
+def extract_operands(source):
+    """Extract operands from a decimal, a float or an int, according to
+    `CLDR rules`_.
+
+    .. _`CLDR rules`: http://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Operands
+    """
+    n = abs(source)
+    i = int(n)
+    if isinstance(n, float):
+        n = i if i == n else decimal.Decimal(n)
+
+    if isinstance(n, decimal.Decimal):
+        dec_tuple = n.as_tuple()
+        exp = dec_tuple.exponent
+        fraction_digits = dec_tuple.digits[exp:] if exp < 0 else ()
+        trailing = ''.join(str(d) for d in fraction_digits)
+        no_trailing = trailing.rstrip('0')
+        v = len(trailing)
+        w = len(no_trailing)
+        f = int(trailing or 0)
+        t = int(no_trailing or 0)
+    else:
+        v = w = f = t = 0
+    return n, i, v, w, f, t
+
+
 class PluralRule(object):
     """Represents a set of language pluralization rules.  The constructor
     accepts a list of (tag, expr) tuples or a dict of `CLDR rules`_. The
@@ -106,7 +132,7 @@ class PluralRule(object):
     def __call__(self, n):
         if not hasattr(self, '_func'):
             self._func = to_python(self)
-        return self._func(n)
+        return self._func(*extract_operands(n))
 
 
 def to_javascript(rule):
@@ -156,12 +182,15 @@ def to_python(rule):
         'WITHIN':   within_range_list,
         'MOD':      cldr_modulo
     }
-    to_python = _PythonCompiler().compile
-    result = ['def evaluate(n):']
+    to_python_func = _PythonCompiler().compile
+    result = [
+        'def evaluate(n, v=0, w=0, f=0, t=0):',
+        ' i = int(n)',
+    ]
     for tag, ast in PluralRule.parse(rule).abstract:
         # the str() call is to coerce the tag to the native string.  It's
         # a limited ascii restricted set of tags anyways so that is fine.
-        result.append(' if (%s): return %r' % (to_python(ast), str(tag)))
+        result.append(' if (%s): return %r' % (to_python_func(ast), str(tag)))
     result.append(' return %r' % _fallback_tag)
     code = compile('\n'.join(result), '<rule>', 'exec')
     eval(code, namespace)
index 122d64d77df4d2e737b0412b37c3108a674ad6d1..ece1358c5ea3696db99e8ac397c27d9f924f159e 100644 (file)
 # This software consists of voluntary contributions made by many
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://babel.edgewall.org/log/.
+import decimal
 
 import doctest
 import unittest
 import pytest
-
+from decimal import Decimal as Dec
 from babel import plural
 
 
@@ -123,7 +124,7 @@ def test_tokenize_well_formed(rule_text, tokens):
 
 
 MALFORMED_TOKEN_TESTS = (
-    ('a = 1'), ('n ! 2'),
+    'a = 1', 'n ! 2',
 )
 
 
@@ -201,3 +202,21 @@ class PluralRuleParserTestCase(unittest.TestCase):
                                                     plural.value_node(100))),
                                            (make_range_list((1, 19)))))))
                     ))
+
+
+EXTRACT_OPERANDS_TESTS = (
+    (1, 1, 1, 0, 0, 0, 0),
+    ('1.0', '1.0', 1, 1, 0, 0, 0),
+    ('1.00', '1.00', 1, 2, 0, 0, 0),
+    ('1.3', '1.3', 1, 1, 1, 3, 3),
+    ('1.30', '1.30', 1, 2, 1, 30, 3),
+    ('1.03', '1.03', 1, 2, 2, 3, 3),
+    ('1.230', '1.230', 1, 3, 2, 230, 23),
+    (-1, 1, 1, 0, 0, 0, 0),
+)
+
+
+@pytest.mark.parametrize('source,n,i,v,w,f,t', EXTRACT_OPERANDS_TESTS)
+def test_extract_operands(source, n, i, v, w, f, t):
+    assert (plural.extract_operands(decimal.Decimal(source)) ==
+            decimal.Decimal(n), i, v, w, f, t)