# HG changeset patch # User cmlenz # Date 1219664959 0 # Node ID a0fdc4c4a169c70b05936bf0ec2718e5f3787366 # Parent d2b5fa531a58b1b83d434199b56246fc60933bd3 More plural module cleanup and fixes. diff --git a/babel/plural.py b/babel/plural.py --- a/babel/plural.py +++ b/babel/plural.py @@ -19,18 +19,21 @@ except NameError: from sets import ImmutableSet as frozenset, Set as set +__all__ = ['PluralRule', 'RuleError', 'to_gettext', 'to_javascript', + 'to_python'] +__docformat__ = 'restructuredtext en' + _plural_tags = ('zero', 'one', 'two', 'few', 'many', 'other') _fallback_tag = 'other' class PluralRule(object): - """Represents a CLDR language pluralization rules. The constructors - accepts a list of (tag, expr) tuples or a dict of CLDR rules. - The resulting object is callable and accepts one parameter with a - positive or negative number (both integer and float) for the number - that indicates the plural form for a string and returns the tag for - the format: + """Represents a set of language pluralization rules. The constructor + accepts a list of (tag, expr) tuples or a dict of CLDR rules. The + resulting object is callable and accepts one parameter with a positive or + negative number (both integer and float) for the number that indicates the + plural form for a string and returns the tag for the format: >>> rule = PluralRule({'one': 'n is 1'}) >>> rule(1) @@ -42,16 +45,18 @@ other where other is an implicit default. Rules should be mutually exclusive; for a given numeric value, only one rule should apply (i.e. the condition should only be true for one of the plural rule elements. - - :param rules: a list of ``(tag, expr)``) tuples with the rules conforming - to UTS #35 or a dict with the tags as keys and expressions - as values. - :raise: a `RuleError` if the expression is malformed """ __slots__ = ('abstract', '_func') def __init__(self, rules): + """Initialize the rule instance. + + :param rules: a list of ``(tag, expr)``) tuples with the rules + conforming to UTS #35 or a dict with the tags as keys + and expressions as values. + :raise RuleError: if the expression is malformed + """ if isinstance(rules, dict): rules = rules.items() found = set() @@ -86,29 +91,16 @@ parse = classmethod(parse) def rules(self): - """The `PluralRule` as a dict of unicode plural rules.""" + """The `PluralRule` as a dict of unicode plural rules. + + >>> rule = PluralRule({'one': 'n is 1'}) + >>> rule.rules + {'one': 'n is 1'} + """ _compile = _UnicodeCompiler().compile return dict([(tag, _compile(ast)) for tag, ast in self.abstract]) rules = property(rules, doc=rules.__doc__) - def gettext_expr(self): - """The plural rule as gettext expression. The gettext expression is - technically limited to integers and returns indices rather than tags. - - >>> PluralRule({'one': 'n is 1', 'two': 'n is 2'}).gettext_expr - 'nplurals=3; plural=((n == 2) ? 1 : (n == 1) ? 0 : 2)' - """ - used_tags = self.tags | set([_fallback_tag]) - _compile = _GettextCompiler().compile - _get_index = [tag for tag in _plural_tags if tag in used_tags].index - - result = ['nplurals=%d; plural=(' % len(used_tags)] - for tag, ast in self.abstract: - result.append('%s ? %d : ' % (_compile(ast), _get_index(tag))) - result.append('%d)' % _get_index(_fallback_tag)) - return ''.join(result) - gettext_expr = property(gettext_expr, doc=gettext_expr.__doc__) - tags = property(lambda x: frozenset([i[0] for i in x.abstract]), doc=""" A set of explicitly defined tags in this rule. The implicit default ``'other'`` rules is not part of this set unless there is an explicit @@ -138,7 +130,7 @@ advantage that external helper functions are not required and is not a big performance hit for these simple calculations. - :param rules: the rules as list or dict, or a `PluralRule` object + :param rule: the rules as list or dict, or a `PluralRule` object :return: a corresponding JavaScript function as `str` :raise RuleError: if the expression is malformed """ @@ -161,7 +153,7 @@ >>> func(3) 'few' - :param rules: the rules as list or dict, or a `PluralRule` object + :param rule: the rules as list or dict, or a `PluralRule` object :return: a corresponding Python function :raise RuleError: if the expression is malformed """ @@ -172,7 +164,7 @@ } to_python = _PythonCompiler().compile result = ['def evaluate(n):'] - for tag, ast in rule.abstract: + for tag, ast in PluralRule.parse(rule).abstract: result.append(' if (%s): return %r' % (to_python(ast), tag)) result.append(' return %r' % _fallback_tag) exec '\n'.join(result) in namespace @@ -185,8 +177,22 @@ >>> to_gettext({'one': 'n is 1', 'two': 'n is 2'}) 'nplurals=3; plural=((n == 2) ? 1 : (n == 1) ? 0 : 2)' + + :param rule: the rules as list or dict, or a `PluralRule` object + :return: an equivalent gettext-style plural expression + :raise RuleError: if the expression is malformed """ - return PluralRule.parse(rule).gettext_expr + rule = PluralRule.parse(rule) + + used_tags = rule.tags | set([_fallback_tag]) + _compile = _GettextCompiler().compile + _get_index = [tag for tag in _plural_tags if tag in used_tags].index + + result = ['nplurals=%d; plural=(' % len(used_tags)] + for tag, ast in rule.abstract: + result.append('%s ? %d : ' % (_compile(ast), _get_index(tag))) + result.append('%d)' % _get_index(_fallback_tag)) + return ''.join(result) def in_range(num, min, max): diff --git a/babel/tests/__init__.py b/babel/tests/__init__.py --- a/babel/tests/__init__.py +++ b/babel/tests/__init__.py @@ -14,7 +14,8 @@ import unittest def suite(): - from babel.tests import core, dates, localedata, numbers, support, util + from babel.tests import core, dates, localedata, numbers, plural, \ + support, util from babel.messages import tests as messages suite = unittest.TestSuite() suite.addTest(core.suite()) @@ -22,6 +23,7 @@ suite.addTest(localedata.suite()) suite.addTest(messages.suite()) suite.addTest(numbers.suite()) + suite.addTest(plural.suite()) suite.addTest(support.suite()) suite.addTest(util.suite()) return suite diff --git a/babel/tests/plural.py b/babel/tests/plural.py new file mode 100644 --- /dev/null +++ b/babel/tests/plural.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2008 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://babel.edgewall.org/wiki/License. +# +# 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 doctest +import unittest + +from babel import plural + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(doctest.DocTestSuite(plural)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite')